You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cmdtypist/noslaclessons.txt

76044 lines
2.4 MiB

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 <<sd kdi skt
n ,dll adcn zdie ytzo digl salg llsd lfll aiam inth eomt nthe olog htis thin gise rall yver ysta rsgh alld ghoe potu jlsa hlah dgja peop oeoo ihgo peoh goeh ahlb clmx bx,c nb,h slga s>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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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 <Automatic>
Addresses 10.104.33.32/16__________ <Remove>
<Hide>
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 <<stop
> 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 <<EOF > text.txt
~]$ cat text.txt
~]$ cat <<brol > 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
/\<he\>
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 <bar> separates the two commands, set number! toggles the state and set
number? reports the current state.
:map <F6> :set number!<bar>set number?<CR>
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 <F6> :set number!<bar>set number?<CR>
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 <stdio.h>
#define __USE_XOPEN
#include <unistd.h>
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 <vojtech@suse.cz>
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 <stdio.h>
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 <stdio.h> 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 <stdio.h> 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 <cntrl> C). Then select paste (or <cntrl> 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 <Enter> 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 <Enter> 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 "<pyshell#34>", 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 <expr>
print <expr>, <expr>, ..., <expr>
print <expr>, <expr>, ..., <expr>,
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:
<variable> = <expr>
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.
<variable> = input(<prompt>)
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:
<var>, <var>, ..., <var> = <expr>, <expr>, ..., <expr>
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 <var> in <sequence>:
<body>
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 <var> in <sequence>: 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(<expr>) will produce a list of numbers that starts with 0 and goes up to, but not
including, the value of <expr>. 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 <variable> in range(<expr>):
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 <sequence>
no
yes
<var> = next item
<body>
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 $%$int*),>
>>> type(3.14)
<type -'+float$,*>
>>> type(3.0)
<type ,/*float,-$>
>>> myInt = -32
>>> type(myInt)
<type ''-int#&">
>>> myFloat = 32.0
>>> type(myFloat)
<type '''float'&+>
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 "<stdin>", 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 "<stdin>", 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 "<stdin>", 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 "<stdin>", 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)
<type ('*long int!.(>
>>> 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 /.%string*+$>
>>> type(str2)
<type ($,string)&!>
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 "<pyshell#8>", line 1,
firstName = input("Please
File "<string>", 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 <string>[<expr>]. 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(<expr>)
int(<expr>)
long(<expr>
str(<expr>)
eval(<string>)
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:
<template-string> % (<values>)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:
%<width>.<precision><type-char>
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.
<filevar> = open(<name>, <mode>)
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:
<filevar>.read()
<filevar>.readline()
<filevar>.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.
<file-var>.write(<string>)
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.
<class-name>(<param1>, <param2>, ...)
Here <class-name> 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.
<object>.<method-name>(<param1>, <param2>, ...)
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 <Enter> 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 <Enter> 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 <Enter> 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 <insert-name>.
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 <Enter> 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 <name>(<formal-parameters>):
<body>
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.
<name>(<actual-parameters>)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 "<stdin>", 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 <Enter> 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 <Enter> 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 <Enter> 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 <condition>:
<body>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
<condition> true?
yes
no
<Body>
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.
<expr> <relop> <expr>
<relop> 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 <myfile>.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 <condition>:
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 "<stdin>", 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 <condition>:
<statements>
else:
<statements>
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 <condition1>:
<case1 statements>
elif <condition2>:
<case2 statements>
elif <condition3>:
<case3 statements>
...
else:
<default statements>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:
<body>
except <ErrorType>:
<handler>
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 "<stdin>", 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 <var> in <sequence>:
<body>
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 <condition>:
<body>
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
<condition>?
no
yes
<body>
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 (<Enter> to quit) >> ")
while xStr != "":
x = eval(xStr)
sum = sum + x
count = count + 1
xStr = raw_input("Enter a number (<Enter> 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
(<Enter>
(<Enter>
(<Enter>
(<Enter>
(<Enter>
(<Enter>
(<Enter>
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.
<expr> and <expr>
<expr> or <expr>
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 <player wins serve>:
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 <condition>:
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 <class-name>:
<method-definitions>
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 (<Enter> to quit) >> ")
while xStr != "":
x = eval(xStr)
sum = sum + x
count = count + 1
xStr = raw_input("Enter a number (<Enter> 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 "<stdin>", 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 (<Enter> to quit) >> ")
while xStr != "":
x = eval(xStr)
nums.append(x)
# add this value to the list
xStr = raw_input("Enter a number (<Enter> 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 (<Enter> to quit) >> ")
while xStr != "":
x = eval(xStr)
nums.append(x)
# add this value to the list
xStr = raw_input("Enter a number (<Enter> 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) = <next item from bSpecs>
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,
<dictionary>[<key>]
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 <Enter> 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$&#117
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 <filename>
from the command line, where <filename> 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 <filename>For the Borland C++ for Windows compiler: bcc <filename>
For the Borland Turbo C++ compiler: tc <filename>
For the Microsoft compilers: cl <filename>
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 <iostream.h>
Listing 1.1. HELLO.CPP, the Hello World program.
1:
2:
3:
4:
5:
6:
7:
#include <iostream.h>
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 <iostream.h>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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h> 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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <<endl;
16: }
Output: Width:5
Length: 10
Area: 50
Analysis: On line 5, USHORT is typedefined as a synonym for unsigned short int. The
program is very much like Listing 3.2, and the output is the same.
When to Use short and When to Use long
One source of confusion for new C++ programmers is when to declare a variable to be type long and
when to declare it to be type short. The rule, when understood, is fairly straightforward: If there is
any chance that the value you'll want to put into your variable will be too big for its type, use a larger
type.
As seen in Table 3.1, unsigned short integers, assuming that they are two bytes, can hold a
value only up to 65,535. Signed short integers can hold only half that. Although unsigned
long integers can hold an extremely large number (4,294,967,295) that is still quite finite. If you
need a larger number, you'll have to go to float or double, and then you lose some precision.
Floats and doubles can hold extremely large numbers, but only the first 7 or 19 digits are significanton most computers. That means that the number is rounded off after that many digits.
Wrapping Around an unsigned Integer
The fact that unsigned long integers have a limit to the values they can hold is only rarely a
problem, but what happens if you do run out of room?
When an unsigned integer reaches its maximum value, it wraps around and starts over, much as a
car odometer might. Listing 3.4 shows what happens if you try to put too large a value into a short
integer.
Listing 3.4.A demonstration of putting too large a value in an unsigned
integer.
1: #include <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <<endl;
14:
return 0;
15: }
Output: Difference is: 50
Now difference is: 4294967246
Analysis: The subtraction operator is invoked on line 10, and the result is printed on line 11, much as
we might expect. The subtraction operator is called again on line 12, but this time a large unsigned
number is subtracted from a small unsigned number. The result would be negative, but because it is
evaluated (and printed) as an unsigned number, the result is an overflow, as described yesterday.
This topic is reviewed in detail in Appendix A, "Operator Precedence."
Integer Division and Modulus
Integer division is somewhat different from everyday division. When you divide 21 by 4, the result is
a real number (a number with a fraction). Integers don't have fractions, and so the "remainder" islopped off. The answer is therefore 5. To get the remainder, you take 21 modulus 4 (21 % 4) and the
result is 1. The modulus operator tells you the remainder after an integer division.
Finding the modulus can be very useful. For example, you might want to print a statement on every
10th action. Any number whose value is 0 when you modulus 10 with that number is an exact
multiple of 10. Thus 1 % 10 is 1, 2 % 10 is 2, and so forth, until 10 % 10, whose result is 0. 11 % 10 is
back to 1, and this pattern continues until the next multiple of 10, which is 20. We'll use this technique
when looping is discussed on Day 7, "More Program Flow."
WARNING: Many novice C++ programmers inadvertently put a semicolon after their if
statements:
if(SomeValue < 10);
SomeValue = 10;
What was intended here was to test whether SomeValue is less than 10, and if so, to set it to
10, making 10 the minimum value for SomeValue. Running this code snippet will show that
SomeValue is always set to 10! Why? The if statement terminates with the semicolon (the
do-nothing operator). Remember that indentation has no meaning to the compiler. This snippet
could more accurately have been written as:
if (SomeValue < 10) // test
; // do nothing
SomeValue = 10; // assign
Removing the semicolon will make the final line part of the if statement and will make this
code do what was intended.
Combining the Assignment and Mathematical Operators
It is not uncommon to want to add a value to a variable, and then to assign the result back into the
variable. If you have a variable myAge and you want to increase the value by two, you can write
int myAge = 5;
int temp;
temp = myAge + 2;
myAge = temp;
// add 5 + 2 and put it in temp
// put it back in myAge
This method, however, is terribly convoluted and wasteful. In C++, you can put the same variable on
both sides of the assignment operator, and thus the preceding becomes
myAge = myAge + 2;which is much better. In algebra this expression would be meaningless, but in C++ it is read as "add
two to the value in myAge and assign the result to myAge."
Even simpler to write, but perhaps a bit harder to read is
myAge += 2;
The self-assigned addition operator (+=) adds the rvalue to the lvalue and then reassigns the result
into the lvalue. This operator is pronounced "plus-equals." The statement would be read "myAge plus-
equals two." If myAge had the value 4 to start, it would have 6 after this statement.
There are self-assigned subtraction (-=), division (/=), multiplication (*=), and modulus (%=)
operators as well.
Increment and Decrement
The most common value to add (or subtract) and then reassign into a variable is 1. In C++, increasing
a value by 1 is called incrementing, and decreasing by 1 is called decrementing. There are special
operators to perform these actions.
The increment operator (++) increases the value of the variable by 1, and the decrement operator (--)
decreases it by 1. Thus, if you have a variable, C, and you want to increment it, you would use this
statement:
C++;
// Start with C and increment it.
This statement is equivalent to the more verbose statement
C = C + 1;
which you learned is also equivalent to the moderately verbose statement
C += 1;
Prefix and Postfix
Both the increment operator (++) and the decrement operator(--) come in two varieties: prefix and
postfix. The prefix variety is written before the variable name (++myAge); the postfix variety is
written after (myAge++).
In a simple statement, it doesn't much matter which you use, but in a complex statement, when you
are incrementing (or decrementing) a variable and then assigning the result to another variable, it
matters very much. The prefix operator is evaluated before the assignment, the postfix is evaluatedafter.
The semantics of prefix is this: Increment the value and then fetch it. The semantics of postfix is
different: Fetch the value and then increment the original.
This can be confusing at first, but if x is an integer whose value is 5 and you write
int a = ++x;
you have told the compiler to increment x (making it 6) and then fetch that value and assign it to a.
Thus, a is now 6 and x is now 6.
If, after doing this, you write
int b = x++;
you have now told the compiler to fetch the value in x (6) and assign it to b, and then go back and
increment x. Thus, b is now 6, but x is now 7. Listing 4.3 shows the use and implications of both
types.
Listing 4.3. A demonstration of prefix and postfix operators.
// Listing 4.3 - demonstrates use of
// prefix and postfix increment and
// decrement operators
#include <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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<SomeValue; i++) are scoped to the block in
which the for loop is created, but there is talk of changing this in the official C++ standard.
None of this matters very much if you are careful not to reuse your variable names within any given
function.Global Variables
Variables defined outside of any function have global scope and thus are available from any function
in the program, including main().
Local variables with the same name as global variables do not change the global variables. A local
variable with the same name as a global variable hides the global variable, however. If a function has
a variable with the same name as a global variable, the name refers to the local variable--not the
global--when used within the function. Listing 5.3 illustrates these points.
Listing 5.3. Demonstrating global and local variables .
1:
#include <iostream.h>
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 <iostream.h>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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
// 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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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<rows; i++)
{
for (int j = 0; j<columns; j++)
cout << theChar;
cout << "\n";
}
return 0;
Output: How many rows? 4
How many columns? 12
What character? x
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
Analysis: The user is prompted for the number of rows and columns and for a character to print.
The first for loop, on line 16, initializes a counter (i) to 0, and then the body of the outer for loop is
run.
On line 18, the first line of the body of the outer for loop, another for loop is established. A second
counter (j) is also initialized to 0, and the body of the inner for loop is executed. On line 19, the
chosen character is printed, and control returns to the header of the inner for loop. Note that the inner
for loop is only one statement (the printing of the character). The condition is tested (j <
columns) and if it evaluates true, j is incremented and the next character is printed. This
continues until j equals the number of columns.
Once the inner for loop fails its test, in this case after 12 Xs are printed, execution falls through to
line 20, and a new line is printed. The outer for loop now returns to its header, where its condition (i
< rows) is tested. If this evaluates true, i is incremented and the body of the loop is executed.
In the second iteration of the outer for loop, the inner for loop is started over. Thus, j is
reinitialized to 0 and the entire inner loop is run again.
The important idea here is that by using a nested loop, the inner loop is executed for each iteration of
the outer loop. Thus the character is printed columns times for each row.
NOTE: As an aside, many C++ programmers use the letters i and j as counting
variables. This tradition goes all the way back to FORTRAN, in which the letters i, j,k, l, m, and n were the only legal counting variables. Other programmers prefer to use
more descriptive counter variable names, such as Ctrl and Ctr2. Using i and j in
for loop headers should not cause much confusion, however.
Scoping in for Loops
You will remember that variables are scoped to the block in which they are created. That is, a local
variable is visible only within the block in which it is created. It is important to note that counting
variables created in the header of a for loop are scoped to the outer block, not the inner block. The
implication of this is that if you have two for loops in the same function, you must give them
different counter variables, or they may interfere with one another.
Summing Up Loops
On Day 5, "Functions," you learned how to solve the Fibonacci series problem using recursion. To
review briefly, a Fibonacci series starts with 1, 1, 2, 3, and all subsequent numbers are the sum of the
previous two:
1,1,2,3,5,8,13,21,34...
The nth Fibonacci number is the sum of the n-1 and the n-2 Fibonacci numbers. The problem solved
on Day 5 was finding the value of the nth Fibonacci number. This was done with recursion. Listing
7.15 offers a solution using iteration.
Listing 7.15. Solving the nth Fibonacci numberusing iteration.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
// Listing 7.15
// Demonstrates solving the nth
// Fibonacci number using iteration
#include <iostream.h>
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 <iostream.h>
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 <iostream.h>
// 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 <iostream.h>
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<height; i++)
121:
{
122:
for (USHORT j = 0; j< width; j++)
123:
cout << "*";
124:
cout << "\n";
125:
}
126: }
127:
128:129: void DoGetArea(Rectangle theRect)
130: {
131:
cout << "Area: " << theRect.GetArea() << endl;
132: }
133:
134: void DoGetPerim(Rectangle theRect)
135: {
136:
cout << "Perimeter: " << theRect.GetPerim() << endl;
137: }
Output: *** Menu ***
(1) Draw Rectangle
(2) Area
(3) Perimeter
(4) Resize
(5) Quit
1
******************************
******************************
******************************
******************************
******************************
*** Menu ***
Draw Rectangle
Area
Perimeter
Resize
Quit
(1)
(2)
(3)
(4)
(5)
2
Area: 150
*** Menu ***
Draw Rectangle
Area
Perimeter
Resize
Quit
(1)
(2)
(3)
(4)
(5)
3
Perimeter: 70
(1)
(2)
(3)
(4)
(5)
4
*** Menu ***
Draw Rectangle
Area
Perimeter
Resize
QuitNew Width: 10
New height: 8
**********
**********
**********
**********
**********
**********
**********
**********
*** Menu ***
Draw Rectangle
Area
Perimeter
Resize
Quit
(1)
(2)
(3)
(4)
(5)
2
Area: 80
*** Menu ***
Draw Rectangle
Area
Perimeter
Resize
Quit
(1)
(2)
(3)
(4)
(5)
3
Perimeter: 36
(1)
(2)
(3)
(4)
(5)
5
*** Menu ***
Draw Rectangle
Area
Perimeter
Resize
Quit
Exiting...
Analysis: This program utilizes most of the skills you learned this week. You should not only be able
to enter, compile, link, and run this program, but also understand what it does and how it works, based
on the work you've done this week.
The first six lines set up the new types and definitions that will be used throughout the program.
Lines 9-29 declare the Rectangle class. There are public accessor methods for obtaining and setting
the width and height of the rectangle, as well as for computing the area and perimeter. Lines 32-43contain the class function definitions that were not declared inline.
The function prototypes, for the non-class member functions, are on lines 47-50, and the program
begins on line 52. The essence of this program is to generate a rectangle, and then to print out a menu
offering five options: Draw the rectangle, determine its area, determine its perimeter, resize the
rectangle, or quit.
A flag is set on line 58, and when that flag is not set to TRUE the menu loop continues. The flag is only
set to TRUE if the user picks Quit from the menu.
Each of the other choices, with the exception of ChangeDimensions, calls out to a function. This
makes the switch statement cleaner. ChangeDimensions cannot call out to a function because it
must change the dimensions of the rectangle. If the rectangle were passed (by value) to a function such
as DoChangeDimensions(), the dimensions would be changed on the local copy of the rectangle
in DoChangeDimensions() and not on the rectangle in main(). On Day 8, "Pointers," and Day
10, "Advanced Functions," you'll learn how to overcome this restriction, but for now the change is
made in the main() function.
Note how the use of an enumeration makes the switch statement much cleaner and easier to
understand. Had the switch depended on the numeric choices (1-5) of the user, you would have to
constantly refer to the description of the menu to see which pick was which.
On line 63, the user's choice is checked to make sure it is in range. If not, an error message is printed
and the menu is reprinted. Note that the switch statement includes an "impossible" default condition.
This is an aid in debugging. If the program is working, that statement can never be reached.
Week in Review
Congratulations! You've completed the first week! Now you can create and understand sophisticated
C++ programs. Of course, there's much more to do, and next week starts with one of the most difficult
concepts in C++: pointers. Don't give up now, you're about to delve deeply into the meaning and use of
object-oriented programming, virtual functions, and many of the advanced features of this powerful
language.
Take a break, bask in the glory of your accomplishment, and then turn the page to start Week
Pointers present two special challenges when learning C++: They can be somewhat confusing, and it isn't
immediately obvious why they are needed. This chapter explains how pointers work, step by step. You will
fully understand the need for pointers, however, only as the book progresses.
What Is a Pointer?
New Term: A pointer is a variable that holds a memory address.
To understand pointers, you must know a little about computer memory. Computer memory is divided into
sequentially numbered memory locations. Each variable is located at a unique location in memory, known as
its address. (This is discussed in the "Extra Credit" section following Day 5, "Functions.") Figure 8.1 shows a
schematic representation of the storage of an unsigned long integer variable theAge.
Figure 8.1. A schematic representation of theAge.
Different computers number this memory using different, complex schemes. Usually programmers don't need
to know the particular address of any given variable, because the compiler handles the details. If you want this
information, though, you can use the address of operator (&), which is illustrated in Listing 8.1.
Listing 8.1. Demonstrating address of variables.
1:
2:
3:
4:
5:
6:
7:
8:
// Listing 8.1 Demonstrates address of operator
// and addresses of local variables
#include <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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<height; i++)45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64: }
{
for (USHORT j = 0; j< width; j++)
{
cout << "*";
}
cout << "\n";
}
}
// Driver program to demonstrate overloaded functions
int main()
{
// initialize a rectangle to 30,5
Rectangle theRect(30,5);
cout << "DrawShape(): \n";
theRect.DrawShape();
cout << "\nDrawShape(40,2): \n";
theRect.DrawShape(40,2);
return 0;
NOTE: This listing passes width and height values to several functions. You
should note that sometimes width is passed first and at other times height is passed
first.
Output: DrawShape():
Analysis: Listing 10.1 represents a stripped-down version of the Week in Review project from Week
1. The test for illegal values has been taken out to save room, as have some of the accessor functions.
The main program has been stripped down to a simple driver program, rather than a menu.
The important code, however, is on lines 16 and 17, where DrawShape() is overloaded. The
implementation for these overloaded class methods is on lines 32-52. Note that the version of
DrawShape() that takes no parameters simply calls the version that takes two parameters, passing
in the current member variables. Try very hard to avoid duplicating code in two functions. Otherwise,keeping them in sync when changes are made to one or the other will be difficult and error-prone.
The driver program, on lines 54-64, creates a rectangle object and then calls DrawShape(), first
passing in no parameters, and then passing in two unsigned short integers.
The compiler decides which method to call based on the number and type of parameters entered. One
can imagine a third overloaded function named DrawShape() that takes one dimension and an
enumeration for whether it is the width or height, at the user's choice.
Using Default Values
Just as non-class functions can have one or more default values, so can each member function of a
class. The same rules apply for declaring the default values, as illustrated in Listing 10.2.
Listing 10.2. Using default values.
1:
//Listing 10.2 Default values in member functions
2:
#include <iostream.h>
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<printHeight; i++)
{
for (int j = 0; j< printWidth; j++)
{
cout << "*";
}
cout << "\n";
}
}
// Driver program to demonstrate overloaded functions
int main()
{
// initialize a rectangle to 10,20
Rectangle theRect(30,5);
cout << "DrawShape(0,0,TRUE)...\n";
theRect.DrawShape(0,0,TRUE);
cout <<"DrawShape(40,2)...\n";
theRect.DrawShape(40,2);
return 0;
Output: DrawShape(0,0,TRUE)...
Analysis: Listing 10.2 replaces the overloaded DrawShape() function with a single function with
default parameters. The function is declared on line 14 to take three parameters. The first two,
aWidth and aHeight, are USHORTs, and the third, UseCurrentVals, is a BOOL (true or false)
that defaults to FALSE.
NOTE: Boolean values are those that evaluate to TRUE or FALSE. C++ considers 0 to
be false and all other values to be true.
The implementation for this somewhat awkward function begins on line 29. The third parameter,
UseCurrentValue, is evaluated. If it is TRUE, the member variables itsWidth and
itsHeight are used to set the local variables printWidth and printHeight, respectively.
If UseCurrentValue is FALSE, either because it has defaulted FALSE or was set by the user, the
first two parameters are used for setting printWidth and printHeight.
Note that if UseCurrentValue is TRUE, the values of the other two parameters are completely
ignored.
Choosing Between Default Values and Overloaded Functions
Listings 10.1 and 10.2 accomplish the same thing, but the overloaded functions in Listing 10.1 are
easier to understand and more natural to use. Also, if a third variation is needed--perhaps the user
wants to supply either the width or the height, but not both--it is easy to extend the overloaded
functions. The default value, however, will quickly become unusably complex as new variations are
added.
How do you decide whether to use function overloading or default values? Here's a rule of thumb:
Use function overloading when
There is no reasonable default value.
You need different algorithms.
You need to support variant types in your parameter list.
The Default Constructor
As discussed on Day 6, "Basic Classes," if you do not explicitly declare a constructor for your class, a
default constructor is created that takes no parameters and does nothing. You are free to make your
own default constructor, however, that takes no arguments but that "sets up" your object as required.
The constructor provided for you is called the "default" constructor, but by convention so is any
constructor that takes no parameters. This can be a bit confusing, but it is usually clear from context
which is meant.
Take note that if you make any constructors at all, the default constructor is not made by the compiler.
So if you want a constructor that takes no parameters and you've created any other constructors, you
must make the default constructor yourself!
Overloading Constructors
The point of a constructor is to establish the object; for example, the point of a Rectangle
constructor is to make a rectangle. Before the constructor runs, there is no rectangle, just an area of
memory. After the constructor finishes, there is a complete, ready-to-use rectangle object.
Constructors, like all member functions, can be overloaded. The ability to overload constructors is
very powerful and very flexible.
For example, you might have a rectangle object that has two constructors: The first takes a length
and a width and makes a rectangle of that size. The second takes no values and makes a default-sized
rectangle. Listing 10.3 implements this idea.
Listing 10.3. Overloading the constructor.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
// Listing 10.3
// Overloading constructors
#include <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <class>[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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
#include <string.h>
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 <iostream.h>
#include <string.h>
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 <iostream.h>
#include <string.h>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; i++)
itsString[i] = cString[i];
itsString[itsLen]='\0';
}
// copy constructor
String::String (const String & rhs)
{
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (unsigned short i = 0; i<itsLen;i++)
itsString[i] = rhs[i];
itsString[itsLen] = `\0';
}
// destructor, frees allocated memory
String::~String ()
{
delete [] itsString;
itsLen = 0;
}
// operator equals, frees existing memory
// then copies string and size
String& String::operator=(const String & rhs)
{
if (this == &rhs)
return *this;
delete [] itsString;
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (unsigned short i = 0; i<itsLen;i++)
itsString[i] = rhs[i];
itsString[itsLen] = `\0';
return *this;
}
//nonconstant offset operator, returns
// reference to character so it can be
// changed!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:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
char & String::operator[](unsigned short offset)
{
if (offset > 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<itsLen; i++)
temp[i] = itsString[i];
for (unsigned short j = 0; j<rhs.GetLen(); j++, i++)
temp[i] = rhs[j];
temp[totalLen]='\0';
return temp;
}
// changes current string, returns nothing
void String::operator+=(const String& rhs)
{
unsigned short rhsLen = rhs.GetLen();
unsigned short totalLen = itsLen + rhsLen;
String temp(totalLen);
for (unsigned short i = 0; i<itsLen; i++)
temp[i] = itsString[i];
for (unsigned short j = 0; j<rhs.GetLen(); j++, i++)
temp[i] = rhs[i-itsLen];
temp[totalLen]='\0';
*this = temp;
}143:
int main()
144:
{
145:
String s1("initial test");
146:
cout << "S1:\t" << s1.GetString() << endl;
147:
148:
char * temp = "Hello World";
149:
s1 = temp;
150:
cout << "S1:\t" << s1.GetString() << endl;
151:
152:
char tempTwo[20];
153:
strcpy(tempTwo,"; nice to be here!");
154:
s1 += tempTwo;
155:
cout << "tempTwo:\t" << tempTwo << endl;
156:
cout << "S1:\t" << s1.GetString() << endl;
157:
158:
cout << "S1[4]:\t" << s1[4] << endl;
159:
s1[4]='x';
160:
cout << "S1:\t" << s1.GetString() << endl;
161:
162:
cout << "S1[999]:\t" << s1[999] << endl;
163:
164:
String s2(" Another string");
165:
String s3;
166:
s3 = s1+s2;
167:
cout << "S3:\t" << s3.GetString() << endl;
168:
169:
String s4;
170:
s4 = "Why does this work?";
171:
cout << "S4:\t" << s4.GetString() << endl;
172:
return 0;
173: }
Output: S1:
initial test
S1:
Hello world
tempTwo:
; nice to be
S1:
Hello world; nice to
S1[4]:
o
S1:
Hellx World; nice to
S1[999]:
!
S3:
Hellx World; nice to
S4:
Why does this work?
here!
be here!
be here!
be here! Another string
Analysis: Lines 7-31 are the declaration of a simple String class. Lines 11-13 contain three
constructors: the default constructor, the copy constructor, and a constructor that takes an existing null-
terminated (C-style) string.
This String class overloads the offset operator ([ ]), operator plus (+), and operator plus-equals(+=). The offset operator is overloaded twice: once as a constant function returning a char and again
as a nonconstant function returning a reference to a char.
The nonconstant version is used in statements such as
SomeString[4]='x';
as seen in line 159. This enables direct access to each of the characters in the string. A reference to the
character is returned so that the calling function can manipulate it.
The constant version is used when a constant String object is being accessed, such as in the
implementation of the copy constructor, (line 63). Note that rhs[i] is accessed, yet rhs is declared
as a const String &. It isn't legal to access this object by using a nonconstant member function.
Therefore, the reference operator must be overloaded with a constant accessor.
If the object being returned were large, you might want to declare the return value to be a constant
reference. However, because a char is only one byte, there would be no point in doing that.
The default constructor is implemented in lines 33-39. It creates a string whose length is 0. It is the
convention of this String class to report its length not counting the terminating null. This default
string contains only a terminating null.
The copy constructor is implemented in lines 63-70. It sets the new string's length to that of the
existing string--plus 1 for the terminating null. It copies each character from the existing string to the
new string, and it null-terminates the new string.
Lines 53-60 implement the constructor that takes an existing C-style string. This constructor is similar
to the copy constructor. The length of the existing string is established by a call to the standard
String library function strlen().
On line 28, another constructor, String(unsigned short), is declared to be a private member
function. It is the intent of the designer of this class that no client class ever create a String of
arbitrary length. This constructor exists only to help in the internal creation of Strings as required,
for example, by operator+=, on line 130. This will be discussed in depth when operator+= is
described, below.
The String(unsigned short) constructor fills every member of its array with NULL.
Therefore, the for loop checks for i<=len rather than i<len.
The destructor, implemented in lines 73-77, deletes the character string maintained by the class. Be
sure to include the brackets in the call to the delete operator, so that every member of the array is
deleted, instead of only the first.
The assignment operator first checks whether the right-hand side of the assignment is the same as the
left-hand side. If it isn't, the current string is deleted, and the new string is created and copied intoplace. A reference is returned to facilitate assignments lik
String1 = String2 = String3;
The offset operator is overloaded twice. Rudimentary bounds checking is performed both times. If the
user attempts to access a character at a location beyond the end of the array, the last character--that is,
len-1--is returned.
Lines 117-127 implement operator plus (+) as a concatenation operator. It is convenient to be able to
write
String3 = String1 + String2;
and have String3 be the concatenation of the other two strings. To accomplish this, the operator
plus function computes the combined length of the two strings and creates a temporary string temp.
This invokes the private constructor, which takes an integer, and creates a string filled with nulls. The
nulls are then replaced by the contents of the two strings. The left-hand side string (*this) is copied
first, followed by the right-hand side string (rhs).
The first for loop counts through the string on the left-hand side and adds each character to the new
string. The second for loop counts through the right-hand side. Note that i continues to count the
place for the new string, even as j counts into the rhs string.
Operator plus returns the temp string by value, which is assigned to the string on the left-hand side of
the assignment (string1). Operator += operates on the existing string--that is, the left-hand side of
the statement string1 += string2. It works just like operator plus, except that the temp value
is assigned to the current string (*this = temp) in line 140.
The main()function (lines 143-173) acts as a test driver program for this class. Line 145 creates a
String object by using the constructor that takes a null-terminated C-style string. Line 146 prints its
contents by using the accessor function GetString(). Line 148 creates another C-style string. Line
149 tests the assignment operator, and line 150 prints the results.
Line 152 creates a third C-style string, tempTwo. Line 153 invokes strcpy to fill the buffer with the
characters ; nice to be here! Line 154 invokes operator += and concatenates tempTwo onto
the existing string s1. Line 156 prints the results.
In line 158, the fifth character in s1 is accessed and printed. It is assigned a new value in line 159.
This invokes the nonconstant offset operator ([ ]). Line 160 prints the result, which shows that the
actual value has, in fact, been changed.
Line 162 attempts to access a character beyond the end of the array. The last character of the array is
returned, as designed.
Lines 164-165 create two more String objects, and line 166 calls the addition operator. Line 167prints the results.
Line 169 creates a new String object, s4. Line 170 invokes the assignment operator. Line 171
prints the results. You might be thinking, "The assignment operator is defined to take a constant
String reference in line 21, but here the program passes in a C-style string. Why is this legal?"
The answer is that the compiler expects a String, but it is given a character array. Therefore, it
checks whether it can create a String from what it is given. In line 12, you declared a constructor
that creates Strings from character arrays. The compiler creates a temporary String from the
character array and passes it to the assignment operator. This is known as implicit casting, or
promotion. If you had not declared--and provided the implementation for--the constructor that takes a
character array, this assignment would have generated a compiler error.
Linked Lists and Other Structures
Arrays are much like Tupperware. They are great containers, but they are of a fixed size. If you pick a
container that is too large, you waste space in your storage area. If you pick one that is too small, its
contents spill all over and you have a big mess.
One way to solve this problem is with a linked list. A linked list is a data structure that consists of
small containers that are designed to fit and that are linked together as needed. The idea is to write a
class that holds one object of your data--such as one CAT or one Rectangle--and that can point at
the next container. You create one container for each object that you need to store, and you chain them
together as needed.
The containers are called nodes. The first node in the list is called the head, and the last node in the list
is called the tail.
Lists come in three fundamental forms. From simplest to most complex, they are
)** Singly linked
%,( Doubly linked
"%! Trees
In a singly linked list, each node points forward to the next one, but not backward. To find a particular
node, start at the top and go from node to node, as in a treasure hunt ("The next node is under the
sofa"). A doubly linked list enables you to move backward and forward in the chain. A tree is a
complex structure built from nodes, each of which can point in two or three directions. Figure 11.5
shows these three fundamental structures.
Computer scientists have created even more complex and clever data structures, nearly all of which
rely on interconnecting nodes. Listing 11.13 shows how to create and use a simple linked list.Figure 11.5 Linked lists.
Listing 11.13. Implementing a linked list .
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 11.13
// Linked list simple implementation
#include <iostream.h>
// 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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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<NumAnimalTypes; i++)
{
cout << "(1)dog (2)cat (3)Mammal: ";65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
25:
86: }
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
cin >> 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;i<NumAnimalTypes;i++)
{
theArray[i]->Speak();
OtherArray[i] = theArray[i]->Clone();
}
for (i=0;i<NumAnimalTypes;i++)
OtherArray[i]->Speak();
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 <iostream.h>
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<NumberHorses; i++)
{
cout << "(1)Horse (2)Pegasus: ";
cin >> choice;
if (choice == 2)
pHorse = new Pegasus;
else
pHorse = new Horse;
Ranch[i] = pHorse;
}
cout << "\n";
for (i=0; i<NumberHorses; i++)
{
Ranch[i]->Fly();
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 <iostream.h>
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<NumberHorses; i++)
30:
{
31:
cout << "(1)Horse (2)Pegasus: ";
32:
cin >> 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<NumberHorses; i++)
41:
{
42:
Pegasus *pPeg = dynamic_cast< Pegasus *> (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 <iostream.h>
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<MagicNumber; i++)
{
cout << "\n(1)Horse (2)Pegasus: ";
cin >> choice;
if (choice == 2)
pHorse = new Pegasus;
else
pHorse = new Horse;
Ranch[i] = pHorse;
}
for (i=0; i<MagicNumber; i++)
{
cout << "\n(1)Bird (2)Pegasus: ";
cin >> choice;
if (choice == 2)
pBird = new Pegasus;
else
pBird = new Bird;
Aviary[i] = pBird;
}
cout << "\n";
for (i=0; i<MagicNumber; i++)
{
cout << "\nRanch[" << i << "]: " ;
Ranch[i]->Whinny();
delete Ranch[i];
}
for (i=0; i<MagicNumber; i++)
{
cout << "\nAviary[" << i << "]: " ;
Aviary[i]->Chirp();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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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<itsLength; i++)
56:
{
57:
for (int j = 0; j<itsWidth; j++)
58:
cout << "x ";
59:
60:
cout << "\n";
61:
}
62:
}
63:
64:
class Square : public Rectangle
65:
{
66:
public:
67:
Square(int len);
68:
Square(int len, int width);
69:
~Square(){}
70:
long GetPerim() {return 4 * GetLength();}
71:
};
72:
73:
Square::Square(int len):
74:
Rectangle(len,len)
75:
{}
76:
77:
Square::Square(int len, int width):
78:
Rectangle(len,width)
79:
80:
{
81:
if (GetLength() != GetWidth())82:
cout << "Error, not a square... a Rectangle??\n";
83:
}
84:
85:
int main()
86:
{
87:
int choice;
88:
BOOL fQuit = FALSE;
89:
Shape * sp;
90:
91:
while (1)
92:
{
93:
cout << "(1)Circle (2)Rectangle (3)Square (0)Quit: ";
94:
cin >> 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 <iostream.h>
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<itsLength; i++)
{
for (int j = 0; j<itsWidth; j++)
cout << "x ";
cout << "\n";
}
Shape::Draw();
}
class Square : public Rectangle
{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:
112:
113:
114:
115:
116:
117:
118:
119:
public:
Square(int len);
Square(int len, int width);
~Square(){}
long GetPerim() {return 4 * GetLength();}
};
Square::Square(int len):
Rectangle(len,len)
{}
Square::Square(int len, int width):
Rectangle(len,width)
{
if (GetLength() != GetWidth())
cout << "Error, not a square... a Rectangle??\n";
}
int main()
{
int choice;
BOOL fQuit = FALSE;
Shape * sp;
while (1)
{
cout << "(1)Circle (2)Rectangle (3)Square (0)Quit: ";
cin >> 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 <iostream.h>
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 <iostream.h>
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; i<MaxCats; i++)
CatHouse[i] = new Cat(i);
for (i = 0; i<MaxCats; i++)
{
cout << "There are ";
cout << Cat::HowManyCats;
cout << " cats left!\n";
cout << "Deleting the one which is ";
cout << CatHouse[i]->GetAge();
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 <iostream.h>
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<MaxCats; i++)
{
CatHouse[i] = new Cat(i);
TelepathicFunction();
}
for ( i = 0; i<MaxCats; i++)
{
delete CatHouse[i];
TelepathicFunction();
}
return 0;
}
void TelepathicFunction()
{
cout << "There are ";
cout << Cat::HowManyCats << " cats alive!\n";
Output: There are 1 cats alive!
There are 2 cats alive!
There are 3 cats alive!
There are 4 cats alive!
There are 5 cats alive!
There are 4 cats alive!
There are 3 cats alive!
There are 2 cats alive!
There are 1 cats alive!
There are 0 cats alive!
Analysis: Listing 14.2 is much like Listing 14.1 except for the addition of a new function,
TelepathicFunction(). This function does not create a Cat object, nor does it take a Cat
object as a parameter, yet it can access the HowManyCats member variable. Again, it is worthreemphasizing that this member variable is not in any particular object; it is in the class as a whole,
and, if public, can be accessed by any function in the program.
The alternative to making this member variable public is to make it private. If you do, you can access
it through a member function, but then you must have an object of that class available. Listing 14.3
shows this approach. The alternative, static member functions, is discussed immediately after the
analysis of Listing 14.3.
Listing 14.3. Accessing static members using non-static 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:
//Listing 14.3 private static data members
#include <iostream.h>
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; i<MaxCats; i++)
CatHouse[i] = new Cat(i);
for (i = 0; i<MaxCats; i++)
{
cout << "There are ";
cout << CatHouse[i]->GetHowMany();
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 <iostream.h>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<MaxCats; i++)
27:
{
28:
CatHouse[i] = new Cat(i);
29:
TelepathicFunction();
30:
}
31:
32:
for ( i = 0; i<MaxCats; i++)
33:
{
34:
delete CatHouse[i];
35:
TelepathicFunction();
36:
}
37:
return 0;
38:
}
39:
40:
void TelepathicFunction()
41:
{
42:
cout << "There are " << Cat::GetHowMany() << " cats
alive!\n";
43: }
Output: There are 1 cats alive!
There are 2 cats alive!
There are 3 cats alive!
There are 4 cats alive!
There are 5 cats alive!There
There
There
There
There
are
are
are
are
are
4
3
2
1
0
cats
cats
cats
cats
cats
alive!
alive!
alive!
alive!
alive!
Analysis: The static member variable HowManyCats is declared to have private access on line 15 of
the Cat declaration. The public accessor function, GetHowMany(), is declared to be both public
and static on line 12.
Since GetHowMany() is public, it can be accessed by any function, and since it is static there is no
need to have an object of type Cat on which to call it. Thus, on line 42, the function
TelepathicFunction() is able to access the public static accessor, even though it has no access
to a Cat object. Of course, you could have called GetHowMany() on the Cat objects available in
main(), just as with any other accessor functions.
NOTE: Static member functions do not have a this pointer. Therefore, they cannot be
declared const. Also, because member data variables are accessed in member
functions using the this pointer, static member functions cannot access any non-static
member variables!
Static Member Functions
You can access static member functions by calling them on an object of the class just as you do any
other member function, or you can call them without an object by fully qualifying the class and object
name. Example
class Cat
{
public:
static int GetHowMany() { return HowManyCats; }
private:
static int HowManyCats;
};
int Cat::HowManyCats = 0;
int main()
{
int howMany;
Cat theCat;
// define a cat
howMany = theCat.GetHowMany();
// access through an object
howMany = Cat::GetHowMany();
// access without an object
}Pointers to Functions
Just as an array name is a constant pointer to the first element of the array, a function name is a
constant pointer to the function. It is possible to declare a pointer variable that points to a function,
and to invoke the function by using that pointer. This can be very useful; it allows you to create
programs that decide which functions to invoke based on user input.
The only tricky part about function pointers is understanding the type of the object being pointed to. A
pointer to int points to an integer variable, and a pointer to a function must point to a function of the
appropriate return type and signature.
In the declaration
long (* funcPtr) (int);
funcPtr is declared to be a pointer (note the * in front of the name) that points to a function that
takes an integer parameter and returns a long. The parentheses around * funcPtr are necessary
because the parentheses around int bind more tightly, that is they have higher precedence than the
indirection operator (*). Without the first parentheses this would declare a function that takes an
integer and returns a pointer to a long. (Remember that spaces are meaningless here.)
Examine these two declarations:
long * Function (int);
long (* funcPtr) (int);
The first, Function (), is a function taking an integer and returning a pointer to a variable of type
long. The second, funcPtr, is a pointer to a function taking an integer and returning a variable of
type long.
The declaration of a function pointer will always include the return type and the parentheses
indicating the type of the parameters, if any. Listing 14.5 illustrates the declaration and use of function
pointers.
Listing 14.5. Pointers to functions.
1:
2:
3:
4:
5:
6:
7:
8:
// Listing 14.5 Using function pointers
#include <iostream.h>
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 <iostream.h>
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 <iostream.h>
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<MaxArray;i++)
20:
{
21:
cout << "(1)Change Values (2)Square (3)Cube (4)Swap:
";
22:
cin >> 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<MaxArray; i++)
{
pFuncArray[i](valOne,valTwo);
PrintVals(valOne,valTwo);
}
return 0;
Output: (1)Change Values (2)Square
(1)Change Values (2)Square (3)Cube
(1)Change Values (2)Square (3)Cube
(1)Change Values (2)Square (3)Cube
(1)Change Values (2)Square (3)Cube
New Value for ValOne: 2
New Value for ValTwo: 3
x: 2 y: 3
x: 4 y: 9
x: 64 y: 729
x: 729 y: 64
x: 7153 y:4096
(3)Cube (4)Swap: 1
(4)Swap: 2
(4)Swap: 3
(4)Swap: 4
(4)Swap: 2
Analysis: Once again the implementation of the functions has been left out to save space, but it is the
same as in Listing 14.5. On line 17, the array pFuncArray is de- clared to be an array of 5 pointers
to functions that return void and that take two integer references.
On lines 19-31, the user is asked to pick the functions to invoke, and each member of the array is
assigned the address of the appropriate function. On lines 33-37, each function is invoked in turn. The
result is printed after each invocation.
Passing Pointers to Functions to Other Functions
The pointers to functions (and arrays of pointers to functions, for that matter) can be passed to other
functions, which may take action and then call the right function using the pointer.
For example, you might improve Listing 14.5 by passing the chosen function pointer to another
function (outside of main()), which prints the values, invokes the function, and then prints the
values again. Listing 14.8 illustrates this variation.
WARNING: To compile this program, place lines 46-80 of Listing 14.5 immediately
after line 45.Listing 14.8. Passing pointers to functions as function arguments.
1:
// Listing 14.8 Without function pointers
2:
3:
#include <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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;i<offSet; i++)
195:
pNode = pNode->GetNext();
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 <iostream.h>
#include <string.h>
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; i++)
itsString[i] = cString[i];
itsString[itsLen]='\0';
// cout << "\tString(char*) constructor\n";
// ConstructorCount++;
}
// copy constructor68:
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:
112:
113:
String::String (const String & rhs)
{
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (int i = 0; i<itsLen;i++)
itsString[i] = rhs[i];
itsString[itsLen] = `\0';
// cout << "\tString(String&) constructor\n";
// ConstructorCount++;
}
// destructor, frees allocated memory
String::~String ()
{
delete [] itsString;
itsLen = 0;
// cout << "\tString destructor\n";
}
// operator equals, frees existing memory
// then copies string and size
String& String::operator=(const String & rhs)
{
if (this == &rhs)
return *this;
delete [] itsString;
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (int i = 0; i<itsLen;i++)
itsString[i] = rhs[i];
itsString[itsLen] = `\0';
return *this;
// cout << "\tString operator=\n";
}
//non constant offset operator, returns
// reference to character so it can be
// changed!
char & String::operator[](int offset)
{
if (offset > 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<itsLen; i++)
132:
temp[i] = itsString[i];
133:
for (j = 0; j<rhs.GetLen(); j++, i++)
134:
temp[i] = rhs[j];
135:
temp[totalLen]='\0';
136:
return temp;
137:
}
138:
139:
// changes current string, returns nothing
140:
void String::operator+=(const String& rhs)
141:
{
142:
unsigned short rhsLen = rhs.GetLen();
143:
unsigned short totalLen = itsLen + rhsLen;
144:
String temp(totalLen);
145:
for (int i = 0; i<itsLen; i++)
146:
temp[i] = itsString[i];
147:
for (int j = 0; j<rhs.GetLen(); j++, i++)
148:
temp[i] = rhs[i-itsLen];
149:
temp[totalLen]='\0';
150:
*this = temp;
151:
}
152:
153: // int String::ConstructorCount = 0;
Output: None.
Analysis: Listing 15.1 provides a String class much like the one used in Listing 11.14 of Day 11,
"Arrays." The significant difference here is that the constructors and a few other functions in Listing11.14 have print statements to show their use, which are currently commented out in Listing 15.1.
These functions will be used in later examples.
On line 23, the static member variable ConstructorCount is declared, and on line 153 it is
initialized. This variable is incremented in each string constructor. All of this is currently commented
out; it will be used in a later listing.
Listing 15.2 describes an Employee class that contains three string objects. Note that a number of
statements are commented out; they will be used in later listings.
Listing 15.2. The Employee class and driver 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:
28:
29:
30:
31:
32:
class Employee
{
public:
Employee();
Employee(char *, char *, char *, long);
~Employee();
Employee(const Employee&);
Employee & operator= (const Employee &);
const String & GetFirstName() const
{ return itsFirstName; }
const String & GetLastName() const { return itsLastName;
const String & GetAddress() const { return itsAddress; }
long GetSalary() const { return itsSalary; }
void SetFirstName(const String & fName)
{ itsFirstName = fName; }
void SetLastName(const String & lName)
{ itsLastName = lName; }
void SetAddress(const String & address)
{ itsAddress = address; }
void SetSalary(long salary) { itsSalary = salary; }
private:
String
itsFirstName;
String
itsLastName;
String
itsAddress;
long
itsSalary;
};
Employee::Employee():
itsFirstName(""),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:
itsLastName(""),
itsAddress(""),
itsSalary(0)
{}
Employee::Employee(char * firstName, char * lastName,
char * address, long salary):
itsFirstName(firstName),
itsLastName(lastName),
itsAddress(address),
itsSalary(salary)
{}
Employee::Employee(const Employee & rhs):
itsFirstName(rhs.GetFirstName()),
itsLastName(rhs.GetLastName()),
itsAddress(rhs.GetAddress()),
itsSalary(rhs.GetSalary())
{}
Employee::~Employee() {}
Employee & Employee::operator= (const Employee & rhs)
{
if (this == &rhs)
return *this;
itsFirstName = rhs.GetFirstName();
itsLastName = rhs.GetLastName();
itsAddress = rhs.GetAddress();
itsSalary = rhs.GetSalary();
return *this;
}
int main()
{
Employee Edie("Jane","Doe","1461 Shore Parkway", 20000);
Edie.SetSalary(50000);
String LastName("Levine");
Edie.SetLastName(LastName);
Edie.SetFirstName("Edythe");
cout << "Name: ";
cout << Edie.GetFirstName().GetString();
cout << " " << Edie.GetLastName().GetString();79:
80:
81:
82:
83:
84: }
cout
cout
cout
cout
return
<<
<<
<<
<<
0;
".\nAddress: ";
Edie.GetAddress().GetString();
".\nSalary: " ;
Edie.GetSalary();
NOTE: Put the code from Listing 15.1 into a file called STRING.HPP. Then any time
you need the String class you can include Listing 15.1 by using #include. For
example, at the top of Listing 15.2 add the line #include String.hpp. This will
add the String class to your program.
Output: Name: Edythe Levine.
Address: 1461 Shore Parkway.
Salary: 50000
Analysis: Listing 15.2 shows the Employee class, which contains three string objects:
itsFirstName, itsLastName, and itsAddress.
On line 70, an Employee object is created, and four values are passed in to initialize the Employee
object. On line 71, the Employee access function SetSalary() is called, with the constant value
50000. Note that in a real program this would either be a dynamic value (set at runtime) or a
constant.
On line 72, a string is created and initialized using a C++ string constant. This string object is then
used as an argument to SetLastName() on line 73.
On line 74, the Employee function SetFirstName() is called with yet another string constant.
However, if you are paying close attention, you will notice that Employee does not have a function
SetFirstName() that takes a character string as its argument; SetFirstName() requires a
constant string reference.
The compiler resolves this because it knows how to make a string from a constant character string. It
knows this because you told it how to do so on line 9 of Listing 15.1.
Accessing Members of the Contained Class
Employee objects do not have special access to the member variables of String. If the
Employee object Edie tried to access the member variable itsLen of its own itsFirstName
member variable, it would get a compile-time error. This is not much of a burden, however. The
accessor functions provide an interface for the String class, and the Employee class need not
worry about the implementation details, any more than it worries about how the integer variable,
itsSalary, stores its information.Filtering Access to Contained Members
Note that the String class provides the operator+. The designer of the Employee class has
blocked access to the operator+ being called on Employee objects by declaring that all the string
accessors, such as GetFirstName(), return a constant reference. Because operator+ is not (and
can't be) a const function (it changes the object it is called on), attempting to write the following
will cause a compile-time error:
String buffer = Edie.GetFirstName() + Edie.GetLastName();
GetFirstName() returns a constant String, and you can't call operator+ on a constant
object.
To fix this, overload GetFirstName() to be non-const:
const String & GetFirstName() const { return itsFirstName; }
String & GetFirstName() { return itsFirstName; }
Note that the return value is no longer const and that the member function itself is no longer
const. Changing the return value is not sufficient to overload the function name; you must change
the constancy of the function itself.
Cost of Containment
It is important to note that the user of an Employee class pays the price of each of those string
objects each time one is constructed, or a copy of the Employee is made.
Uncommenting the cout statements in Listing 15.1, lines 38, 51, 63, 75, 84, and 100, reveals how
often these are called. Listing 15.3 rewrites the driver program to add print statements indicating
where in the program objects are being created:
NOTE: To compile this listing, follow these steps: 1. Uncomment lines 38, 51, 63, 75,
84, and 100 in Listing 15.1. 2. Edit Listing 15.2. Remove lines 64-80 and substitute
Listing 15.3. 3. Add #include string.hpp as previously noted.
Listing 15.3. Contained class constructors.
1:
2:
3:
4:
5:
int main()
{
cout << "Creating Edie...\n";
Employee Edie("Jane","Doe","1461 Shore Parkway", 20000);
Edie.SetSalary(20000);6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21: }
cout << "Calling SetFirstName with char *...\n";
Edie.SetFirstName("Edythe");
cout << "Creating temporary string LastName...\n";
String LastName("Levine");
Edie.SetLastName(LastName);
cout
cout
cout
cout
cout
cout
cout
cout
return
<<
<<
<<
<<
<<
<<
<<
<<
0;
"Name: ";
Edie.GetFirstName().GetString();
" " << Edie.GetLastName().GetString();
"\nAddress: ";
Edie.GetAddress().GetString();
"\nSalary: " ;
Edie.GetSalary();
endl;
Output: 1:
Creating Edie...
2:
String(char*) constructor
3:
String(char*) constructor
4:
String(char*) constructor
5:
Calling SetFirstName with char *...
6:
String(char*) constructor
7:
String destructor
8:
Creating temporary string LstName...
9:
String(char*) constructor
10: Name: Edythe Levine
11: Address: 1461 Shore Parkway
12: Salary: 20000
13:
String destructor
14:
String destructor
15:
String destructor
16:
String destructor
Analysis: Listing 15.3 uses the same class declarations as Listings 15.1 and 15.2. However, the cout
statements have been uncommented. The output from Listing 15.3 has been numbered to make
analysis easier.
On line 3 of Listing 15.3, the statement Creating Edie... is printed, as reflected on line 1 of the
output. On line 4 an Employee object, Edie, is created with four parameters. The output reflects the
constructor for String being called three times, as expected.
Line 6 prints an information statement, and then on line 7 is the statement
Edie.SetFirstName("Edythe"). This statement causes a temporary string to be created from
the character string "Edythe", as reflected on lines 6 and 7 of the output. Note that the temporary is
destroyed immediately after it is used in the assignment statement.On line 9, a String object is created in the body of the program. Here the programmer is doing
explicitly what the compiler did implicitly on the previous statement. This time you see the
constructor on line 9 of the output, but no destructor. This object will not be destroyed until it goes out
of scope at the end of the function.
On lines 13-19, the strings in the employee object are destroyed as the Employee object falls out of
scope, and the string LastName, created on line 9, is destroyed as well when it falls out of scope.
Copying by Value
Listing 15.3 illustrates how the creation of one Employee object caused five string constructor calls.
Listing 15.4 again rewrites the driver program. This time the print statements are not used, but the
string static member variable ConstructorCount is uncommented and used.
Examination of Listing 15.1 shows that ConstructorCount is incremented each time a string
constructor is called. The driver program in 15.4 calls the print functions, passing in the
Employee object, first by reference and then by value. ConstructorCount keeps track of how
many string objects are created when the employee is passed as a parameter.
NOTE: To compile this listing: 1. Uncomment lines 23, 39, 52, 64, 76, and 152 in
Listing 15.1. 2. Edit Listing 15.2. Remove lines 68-84 and substitute Listing 15.4. 3.
Add #include string.hpp as previously noted.
Listing 15.4. Passing by value
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
void PrintFunc(Employee);
void rPrintFunc(const Employee&);
int main()
{
Employee Edie("Jane","Doe","1461 Shore Parkway", 20000);
Edie.SetSalary(20000);
Edie.SetFirstName("Edythe");
String LastName("Levine");
Edie.SetLastName(LastName);
cout << "Constructor count: " ;
cout << String::ConstructorCount << endl;
rPrintFunc(Edie);
cout << "Constructor count: ";
cout << String::ConstructorCount << endl;
PrintFunc(Edie);18:
cout << "Constructor count: ";
19:
cout << String::ConstructorCount << endl;
20:
return 0;
21:
}
22:
void PrintFunc (Employee Edie)
23:
{
24:
25:
cout << "Name: ";
26:
cout << Edie.GetFirstName().GetString();
27:
cout << " " << Edie.GetLastName().GetString();
28:
cout << ".\nAddress: ";
29:
cout << Edie.GetAddress().GetString();
30:
cout << ".\nSalary: " ;
31:
cout << Edie.GetSalary();
32:
cout << endl;
33:
34:
}
35:
36:
void rPrintFunc (const Employee& Edie)
37:
{
38:
cout << "Name: ";
39:
cout << Edie.GetFirstName().GetString();
40:
cout << " " << Edie.GetLastName().GetString();
41:
cout << "\nAddress: ";
42:
cout << Edie.GetAddress().GetString();
43:
cout << "\nSalary: " ;
44:
cout << Edie.GetSalary();
45:
cout << endl;
46: }
Output: String(char*) constructor
String(char*) constructor
String(char*) constructor
String(char*) constructor
String destructor
String(char*) constructor
Constructor count: 5
Name: Edythe Levine
Address: 1461 Shore Parkway
Salary: 20000
Constructor count: 5
String(String&) constructor
String(String&) constructor
String(String&) constructor
Name: Edythe Levine.
Address: 1461 Shore Parkway.
Salary: 20000String destructor
String destructor
String destructor
Constructor count: 8
String destructor
String destructor
String destructor
String destructor
Analysis: The output shows that five string objects were created as part of creating one Employee
object. When the Employee object is passed to rPrintFunc() by reference, no additional
Employee objects are created, and so no additional String objects are created. (They too are
passed by reference.)
When, on line 14, the Employee object is passed to PrintFunc() by value, a copy of the
Employee is created, and three more string objects are created (by calls to the copy constructor).
Implementation in Terms of Inheritance/Containment Versus Delegation
At times, one class wants to draw on some of the attributes of another class. For example, let's say you
need to create a PartsCatalog class. The specification you've been given defines a
PartsCatalog as a collection of parts; each part has a unique part number. The PartsCatalog
does not allow duplicate entries, and does allow access by part number.
The listing for the Week in Review for Week 2 provides a LinkedList class. This LinkedList
is well-tested and understood, and you'd like to build on that technology when making your
PartsCatalog, rather than inventing it from scratch.
You could create a new PartsCatalog class and have it contain a LinkedList. The
PartsCatalog could delegate management of the linked list to its contained LinkedList object.
An alternative would be to make the PartsCatalog derive from LinkedList and thereby inherit
the properties of a LinkedList. Remembering, however, that public inheritance provides an is-a
relationship, you should question whether a PartsCatalog really is a type of LinkedList.
One way to answer the question of whether PartsCatalog is a LinkedList is to assume that
LinkedList is the base and PartsCatalog is the derived class, and then to ask these other
questions:
1. Is there anything in the base class that should not be in the derived? For example, does the
LinkedList base class have functions that are inappropriate for the PartsCatalog
class? If so, you probably don't want public inheritance.
2. Might the class you are creating have more than one of the base? For example, might a
PartsCatalog need two LinkedLists in each object? If it might, you almost certainlywant to use containment.
3. Do you need to inherit from the base class so that you can take advantage of virtual
functions or access protected members? If so, you must use inheritance, public or private.
Based on the answers to these questions, you must chose between public inheritance (the is-a
relationship) and either private inheritance or containment.
New Term:
Contained --An object declared as a member of another class contained by that class.
Delegation --Using the attributes of a contained class to accomplish functions not otherwise
available to the containing class.
Implemented in terms of --Building one class on the capabilities of another without using
public inheritance.
Delegation
Why not derive PartsCatalog from LinkedList? The PartsCatalog isn't a LinkedList
because LinkedLists are ordered collections and each member of the collection can repeat. The
PartsCatalog has unique entries that are not ordered. The fifth member of the PartsCatalog
is not part number 5.
Certainly it would have been possible to inherit publicly from PartsList and then override
Insert() and the offset operators ([]) to do the right thing, but then you would have changed the
essence of the PartsList class. Instead you'll build a PartsCatalog that has no offset operator,
does not allow duplicates, and defines the operator+ to combine two sets.
The first way to accomplish this is with containment. The PartsCatalog will delegate list
management to a contained LinkedList. Listing 15.5 illustrates this approach.
Listing 15.5. Delegating to a contained LinkedList.
0:
1:
2:
3:
4:
5:
6:
7:
#include <iostream.h>
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;i<offSet; i++)
pNode = pNode->GetNext();
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 <iostream.h>
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;i<offSet; i++)
pNode = pNode->itsNext;
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 <iostream.h>
#include <string.h>
// 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<itsLen; i++)
temp[i] = itsString[i];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: }
for (int j = 0; j<rhs.GetLen(); j++, i++)
temp[i] = rhs[j];
temp[totalLen]='\0';
return temp;
}
// creates a new string by adding
// one string to another
String operator+(const String& lhs, const String& rhs)
{
int totalLen = lhs.GetLen() + rhs.GetLen();
String temp(totalLen);
for (int i = 0; i<lhs.GetLen(); i++)
temp[i] = lhs[i];
for (int j = 0; j<rhs.GetLen(); j++, i++)
temp[i] = rhs[j];
temp[totalLen]='\0';
return temp;
}
int main()
{
String s1("String One ");
String s2("String Two ");
char *c1 = { "C-String One " } ;
String s3;
String s4;
String s5;
cout
cout
cout
s3 =
cout
s4 =
cout
s5 =
cout
return
<<
<<
<<
s1
<<
s1
<<
c1
<<
0;
"s1: "
"s2: "
"c1: "
+ s2;
"s3: "
+ c1;
"s4: "
+ s1;
"s5: "
<< s1.GetString() << endl;
<< s2.GetString() << endl;
<< c1 << endl;
<< s3.GetString() << endl;
<< s4.GetString() << endl;
<< s5.GetString() << endl;
Output: s1: String One
s2: String Two
c1: C-String One
s3: String One String Two
s4: String One C-String Ones5: C-String One String Two
Analysis: The implementation of all of the string methods except operator+ are unchanged from
Listing 15.1, and so are left out of this listing. On line 20, a new operator+ is overloaded to take
two constant string references and to return a string, and this function is declared to be a friend.
Note that this operator+ is not a member function of this or any other class. It is declared within
the declaration of the String class only so that it can be made a friend, but because it is declared no
other function prototype is needed.
The implementation of this operator+ is on lines 50-60. Note that it is similar to the earlier
operator+, except that it takes two strings and accesses them both through their public accessor
methods.
The driver program demonstrates the use of this function on line 78, where operator+ is now
called on a C-style string!
Friend Functions
Declare a function to be a friend by using the keyword friend and then the full specification of the
function. Declaring a function to be a friend does not give the friend function access to your this
pointer, but it does provide full access to all private and protected member data and functions.
Example
class PartNode
{
// make another class's member function a
friend void PartsList::Insert(Part *);
// make a global function a friend };
friend int SomeFunction();
_friend
Overloading the Insertion Operator
You are finally ready to give your String class the ability to use cout like any other type. Until
now, when you've wanted to print a string, you've been forced to write the following:
cout << theString.GetString();
What you would like to do is write this:
cout << theString;
To accomplish this, you must override operator<<(). Day 16, "Streams," presents the ins and outs
(cins and couts?) of working with iostreams; for now Listing 15.9 illustrates howoperator<< can be overloaded using a friend function.
NOTE: To compile this listing, copy lines 33-153 from Listing 15.1 after line 31 of
Listing 15.9.
Listing 15.9. 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:
32:
33:
34:
35:
36:
37:
#include <iostream.h>
#include <string.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
#include <string.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
#include <iomanip.h>
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 <stdio.h>
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 <fstream.h>
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 <fstream.h>
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 <fstream.h>
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 <iostream.h>
int main(int argc, char **argv)
{
cout << "Received " << argc << " arguments...\n";
for (int i=0; i<argc; i++)
cout << "argument " << i << ": " << argv[i] << endl;
return 0;
Output: TestProgram Teach Yourself C++ In 21 Days
Received 7 arguments...
argumnet 0: TestProgram.exe
argument 1: Teach
argument 2: Yourself
argument 3: C++
argument 4: In
argument 5: 21
argument 6: Days
Analysis: The function main() declares two arguments: argc is an integer that contains the count
of command-line arguments, and argv is a pointer to the array of strings. Each string in the array
pointed to by argv is a command-line argument. Note that argv could just as easily have been
declared as char *argv[] or char argv[][]. It is a matter of programming style how you
declare argv; even though this program declared it as a pointer to a pointer, array offsets were still
used to access the individual strings.
On line 4, argc is used to print the number of command-line arguments: seven in all, counting theprogram name itself.
On lines 5 and 6, each of the command-line arguments is printed, passing the null-terminated strings to
cout by indexing into the array of strings.
A more common use of command-line arguments is illustrated by modifying Listing 16.18 to take the
filename as a command-line argument. This listing does not include the class declaration, which is
unchanged.
Listing 16.20. Using command-line arguments.
1:
#include <fstream.h>
2:
int main(int argc, char *argv[])
// returns 1 on error
3:
{
4:
if (argc != 2)
5:
{
6:
cout << "Usage: " << argv[0] << " <filename>" << 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 <filename>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
#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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
3:
#include <string.h>
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; i++)
itsString[i] = cString[i];
itsString[itsLen]='\0';
ASSERT(Invariants());
}
// copy constructor
String::String (const String & rhs)
{
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (int i = 0; i<itsLen;i++)
itsString[i] = rhs[i];
itsString[itsLen] = `\0';
ASSERT(Invariants());
}
// destructor, frees allocated memory
String::~String ()
{
ASSERT(Invariants());
delete [] itsString;
itsLen = 0;
}
// operator equals, frees existing memory
// then copies string and size
String& String::operator=(const String & rhs)
{
ASSERT(Invariants());
if (this == &rhs)
return *this;
delete [] itsString;
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (int i = 0; i<itsLen;i++)
itsString[i] = rhs[i];
itsString[itsLen] = `\0';
ASSERT(Invariants());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:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
return *this;
}
//non constant offset operator, returns
// reference to character so it can be
// changed!
char & String::operator[](int offset)
{
ASSERT(Invariants());
if (offset > 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 <iostream.h>
#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 <iostream.h>
#include <string.h>
#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 <iostream.h>
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 <iostream.h>
2:
#include <string.h>
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; i++)
itsString[i] = cString[i];
itsString[itsLen]='\0';
// cout << "\tString(char*) constructor\n";
// ConstructorCount++;
}
// copy constructor
String::String (const String & rhs)
{
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (int i = 0; i<itsLen;i++)
itsString[i] = rhs[i];
itsString[itsLen] = `\0';
// cout << "\tString(String&) constructor\n";
// ConstructorCount++;
}
// destructor, frees allocated memory
String::~String ()
{
delete [] itsString;
itsLen = 0;
// cout << "\tString destructor\n";
}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:
130:
131:
132:
133:
134:
135:
// operator equals, frees existing memory
// then copies string and size
String& String::operator=(const String & rhs)
{
if (this == &rhs)
return *this;
delete [] itsString;
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (int i = 0; i<itsLen;i++)
itsString[i] = rhs[i];
itsString[itsLen] = `\0';
return *this;
// cout << "\tString operator=\n";
}
//non constant offset operator, returns
// reference to character so it can be
// changed!
char & String::operator[](int offset)
{
if (offset > 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<itsLen; i++)
temp[i] = itsString[i];136:
for ( j = 0; j<rhs.GetLen(); j++, i++)
137:
temp[i] = rhs[j];
138:
temp[totalLen]='\0';
139:
return temp;
140:
}
141:
142:
void String::operator+=(const String& rhs)
143:
{
144:
unsigned short rhsLen = rhs.GetLen();
145:
unsigned short totalLen = itsLen + rhsLen;
146:
String temp(totalLen);
147:
for (int i = 0; i<itsLen; i++)
148:
temp[i] = itsString[i];
149:
for (int j = 0; j<rhs.GetLen(); j++, i++)
150:
temp[i] = rhs[i-itsLen];
151:
temp[totalLen]='\0';
152:
*this = temp;
153:
}
154:
155:
// int String::ConstructorCount = 0;
156:
157:
ostream& operator<<( ostream& theStream,String& theString)
158:
{
159:
theStream << theString.GetString();
160:
return theStream;
161:
}
162:
163:
class pAddress
164:
{
165:
public:
166:
pAddress(SERVICE theService,
167:
const String& theAddress,
168:
const String& theDisplay):
169:
itsService(theService),
170:
itsAddressString(theAddress),
171:
itsDisplayString(theDisplay)
172:
{}
173:
// pAddress(String, String);
174:
// pAddress();
175:
// pAddress (const pAddress&);
176:
~pAddress(){}
177:
friend ostream& operator<<( ostream& theStream, pAddress&
theAddress);
178:
String& GetDisplayString() { return itsDisplayString; }
179:
private:
180:
SERVICE itsService;181:
String itsAddressString;
182:
String itsDisplayString;
183:
};
184:
185:
ostream& operator<<( ostream& theStream, pAddress&
theAddress)
186:
{
187:
theStream << theAddress.GetDisplayString();
188:
return theStream;
189:
}
190:
191:
class PostMasterMessage
192:
{
193:
public:
194:
// PostMasterMessage();
195:
196:
PostMasterMessage(const pAddress& Sender,
197:
const pAddress& Recipient,
198:
const String& Subject,
199:
const pDate& creationDate);
200:
201:
// other constructors here
202:
// remember to include copy constructor
203:
// as well as constructor from storage
204:
// and constructor from wire format
205:
// Also include constructors from other formats
206:
~PostMasterMessage(){}
207:
208:
void Edit(); // invokes editor on this message
209:
210:
pAddress& GetSender() const { return itsSender; }
211:
pAddress& GetRecipient() const { return itsRecipient; }
212:
String& GetSubject() const { return itsSubject; }
213:
// void SetSender(pAddress& );
214:
// other member accessors
215:
216:
// operator methods here, including operator equals
217:
// and conversion routines to turn PostMaster messages
218:
// into messages of other formats.
219:
220:
private:
221:
pAddress itsSender;
222:
pAddress itsRecipient;
223:
String itsSubject;
224:
pDate itsCreationDate;
225:
pDate itsLastModDate;226:
pDate itsReceiptDate;
227:
pDate itsFirstReadDate;
228:
pDate itsLastReadDate;
229:
};
230:
231:
PostMasterMessage::PostMasterMessage(
232:
const pAddress& Sender,
233:
const pAddress& Recipient,
234:
const String& Subject,
235:
const pDate& creationDate):
236:
itsSender(Sender),
237:
itsRecipient(Recipient),
238:
itsSubject(Subject),
239:
itsCreationDate(creationDate),
240:
itsLastModDate(creationDate),
241:
itsFirstReadDate(0),
242:
itsLastReadDate(0)
243:
{
244:
cout << "Post Master Message created. \n";
245:
}
246:
247:
void PostMasterMessage::Edit()
248:
{
249:
cout << "PostMasterMessage edit function called\n";
250:
}
251:
252:
253:
int main()
254:
{
255:
pAddress Sender(PostMaster, "jliberty@PostMaster", "Jesse
Liberty");
256:
pAddress Recipient(PostMaster, "sl@PostMaster","Stacey
Liberty");
257:
PostMasterMessage PostMessage(Sender, Recipient, "Saying
Hello", 0);
258:
cout << "Message review... \n";
259:
cout << "From:\t\t" << PostMessage.GetSender() << endl;
260:
cout << "To:\t\t" << PostMessage.GetRecipient() << endl;
261:
cout << "Subject:\t" << PostMessage.GetSubject() << endl;
262:
return 0;
263: }
WARNING: If you receive a "can't convert" error, remove the const keywords from
lines 210-212.Output: Post Master Message created.
Message review...
From:
Jesse Liberty
To:
Stacey Liberty
Subject:
Saying Hello
Analysis: On line 4, pDate is type-defined to be an unsigned long. It is not uncommon for
dates to be stored as a long integer (typically as the number of seconds since an arbitrary starting
date such as January 1, 1900). In this program, this is a placeholder; you would expect to
eventually turn pDate into a real class.
On line 5, an enumerated constant, SERVICE, is defined to allow the Address objects to keep track
of what type of address they are, including PostMaster, CompuServe, and so forth.
Lines 7-161 represent the interface to and implementation of String, along much the same lines as
you have seen in previous chapters. The String class is used for a number of member variables in
all of the Message classes and various other classes used by messages, and as such it is pivotal in
your program. A full and robust String class will be essential to making your Message classes
complete.
On lines 162-183, the pAddress class is declared. This represents only the fundamental
functionality of this class, and you would expect to flesh this out once your program is better
understood. These objects represent essential components in every message: both the sender's address
and that of the recipient. A fully functional pAddress object will be able to handle forwarding
messages, replies, and so forth.
It is the pAddress object's job to keep track of the display string as well as the internal routing string
for its service. One open question for your design is whether there should be one pAddress object or
if this should be subclassed for each service type. For now, the service is tracked as an enumerated
constant, which is a member variable of each pAddress object.
Lines 191-229 show the interface to the PostMasterMessage class. In this particular listing, this
class stands on its own, but very soon you'll want to make this part of its inheritance hierarchy. When
you do redesign this to inherit from Message, some of the member variables may move into the base
classes, and some of the member functions may become overrides of base class methods.
A variety of other constructors, accessor functions, and other member functions will be required to
make this class fully functional. Note that what this listing illustrates is that your class does not have
to be 100 percent complete before you can write a simple driver program to test some of your
assumptions.
On lines 247-250, the Edit() function is "stubbed out" in just enough detail to indicate where the
editing functionality will be put once this class is fully operational.Lines 253-263 represent the driver program. Currently this program does nothing more than exercise a
few of the accessor functions and the operator<< overload. Nonetheless, this gives you the starting
point for experimenting with PostMasterMessages and a framework within which you can
modify these classes and examine the impact.
Summary
Today you saw a review of how to bring together many of the elements of C++ syntax and apply them
to object-oriented analysis, design, and programming. The development cycle is not a linear
progression from clean analysis through design and culminating in programming; rather, it is cyclical.
The first phase is typically analysis of the problem, with the results of that analysis forming the basis
for the preliminary design.
Once a preliminary design is complete, programming can begin, but the lessons learned during the
programming phase are fed back into the analysis and design. As programming progresses, testing and
then debugging begins. The cycle continues, never really ending; although discrete points are reached,
at which time it is appropriate to ship the product.
When analyzing a large problem from an object-oriented viewpoint, the interacting parts of the
problem are often the objects of the preliminary design. The designer keeps an eye out for process,
hoping to encapsulate discrete activities into objects whenever possible.
A class hierarchy must be designed, and fundamental relationships among the interacting parts must
be established. The preliminary design is not meant to be final, and functionality will migrate among
objects as the design solidifies.
It is a principal goal of object-oriented analysis to hide as much of the data and implementation as
possible and to build discrete objects that have a narrow and well-defined interface. The clients of
your object should not need to understand the implementation details of how they fulfill their
responsibilities.
Q&A
Q. In what way is object-oriented analysis and design fundamentally different from other
approaches?
A. Prior to the development of these object-oriented techniques, analysts and programmers
tended to think of programs as functions that acted on data. Object-oriented programming
focuses on the integrated data and functionality as discrete units that have both knowledge
(data) and capabilities (functions). Procedural programs, on the other hand, focus on functions
and how they act on data. It has been said that Pascal and C programs are collections of
procedures and C++ programs are collections of classes.
Q. Is object-oriented programming finally the silver bullet that will solve all
programming problems?A. No, it was never intended to be. For large, complex problems, however, object-oriented
analysis, design, and programming can provide the programmer with tools to manage
enormous complexity in ways that were previously impossible.
Q. Is C++ the perfect object-oriented language?
A. C++ has a number of advantages and disadvantages when compared with alternative object-
oriented programming languages, but it has one killer advantage above and beyond all others:
It is the single most popular object-oriented programming language on the face of the Earth.
Frankly, most programmers don't decide to program in C++ after an exhaustive analysis of the
alternative object-oriented programming languages; they go where the action is, and in the
1990s the action is with C++. There are good reasons for that; C++ has a lot to offer, but this
book exists, and I'd wager you are reading it, because C++ is the development language of
choice at so many corporations.
Q. Where can I learn more about object-oriented analysis and design?
A. Day 21 offers some further suggestions, but it is my personal opinion that there are a
number of terrific object-oriented analysis and design books available. My personal favorites
include:
Object-Oriented Analysis and Design with Applications by Grady Booch (2nd Edition).
Published by Benjamin/Cummings Publishing Company, Inc., ISBN: 0-8053-5340-2.
Object-Oriented Modeling and Design by Rumbaugh, Blaha, Premerlani, Eddy, and Lorenson.
Published by Prentice-Hall, ISBN 0-13-629841-9.
There are many other excellent alternatives. Also be sure to join one of the newsgroups or
conferences on the Internet, Interchange, or one of the alternative dial-up services.
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 object-oriented programming and procedural programming?
2. To what does "event-driven" refer?
3. What are the stages in the development cycle?
4. What is a rooted hierarchy?
5. What is a driver program?6. What is encapsulation?
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 if the timing of the traffic signal allows for a smooth flow of traffic.
What kinds of objects should be modeled in the simulation? What would the classes be for the
simulation?
2. Suppose the intersection 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); and taxis, who 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; and 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 stop lights.
How do these considerations change the model?
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.
4. Design and show the interfaces to the classes in the room reservation portion of the program
discussed in Exercise 3.
What Are Templates?
At the end of Week 2, you saw how to build a PartsList object and how to use it to create a
PartsCatalog. If you want to build on the PartsList object to make a list of cats, you have a
problem: PartsList only knows about parts.
To solve this problem, you can create a List base class and derive from it the PartsList and
CatsList classes. You could then cut and paste much of the PartsList class into the new
CatsList declaration. Next week, when you want to make a list of Car objects, you would then
have to make a new class, and again you'd cut and paste.
Needless to say, this is not a satisfactory solution. Over time, the List class and its derived classes
will have to be extended. Making sure that all the changes are propagated to all the related classes
would be a nightmare.
On Day 17, one approach to parameterizing lists was demonstrated briefly--using macros and name
concatenation. Although macros do save much of the cutting and pasting, they have one killer
disadvantage: Like everything else in the preprocessor, they are not type-safe.
Templates offer the preferred method of creating parameterized lists in C++. They are an integrated
part of the language, they are type-safe, and they are very flexible.
Parameterized Types
Templates allow you to teach the compiler how to make a list of any type of thing, rather than creating
a set of type-specific lists--a PartsList is a list of parts, a CatList is a list of cats. The only way
in which they differ is the type of the thing on the list. With templates, the type of the thing on the list
becomes a parameter to the definition of the class.
A common component of virtually all C++ libraries is an array class. As you saw with Lists, it is
tedious and inefficient to create one array class for integers, another for doubles, and yet another for
an array of Animals. Templates let you declare a parameterized array class and then specify whattype of object each instance of the array will hold.
New Term: Instantiation is the act of creating a specific type from a template. The individual
classes are called instances of the template.
Parameterized templates provide you with the ability to create a general class, and pass types as
parameters to that class, in order to build specific instances.
Template Definition
You declare a parameterized Array object (a template for an array) by writing
1:
2:
3:
4:
5:
6:
7:
template <class T>
// 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<int> anIntArray;
Array<Cat> 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<int> 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 <iostream.h>
3:
const int DefaultSize = 10;
4:
5:
template <class T> // 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<T>. For example, if you do not write the
constructor within the class declaration, you must write
template <class T>
Array<T>::Array(int size):
itsSize = size
{
pType = new T[size];
for (int i = 0; i<size; i++)
pType[i] = 0;
}
The declaration on the first line of this code fragment is required to identify the type (class T). The
template name is Array<T>, 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 <iostream.h>
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 <class T> // 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 <class T>
Array<T>::Array(int size = DefaultSize):
itsSize(size)
{
pType = new T[size];
for (int i = 0; i<size; i++)
pType[i] = 0;
}
// copy constructor
template <class T>
Array<T>::Array(const Array &rhs)
{
itsSize = rhs.GetSize();
pType = new T[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
}
// operator=
template <class T>
Array<T>& Array<T>::operator=(const Array &rhs)
{
if (this == &rhs)
return *this;
delete [] pType;
itsSize = rhs.GetSize();
pType = new T[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
return *this;
}
// driver program
int main()
{
Array<int> theArray;
Array<Animal> 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<int>&);
// ok
but you may not write
void SomeFunction(Array<T>&);
// 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 <class T>
void MyTemplateFunction(Array<T>&);
// 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 <class T>
void MyOtherFunction(Array<T>&, Array<int>&);
// 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 <class T> // 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<int>);
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<int> 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<int> theArray;
// an array of integers
42:
Array<Animal> 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<int>";
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<int>
*** 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<T>&);To make this work, we need to declare operator<< to be a template function.
template <class T> ostream& operator<< (ostream&, Array<T>&)
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 <iostream.h>
2:
3:
const int DefaultSize = 10;
4:
5:
template <class T> // 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<T>&);
23:
24:
private:
25:
T *pType;
26:
int itsSize;
27:
};28:
29:
template <class T>
30:
ostream& operator<< (ostream& output, Array<T>& theArray)
31:
{
32:
for (int i = 0; i<theArray.GetSize(); i++)
33:
output << "[" << i << "] " << theArray[i] << endl;
return output;
34:
}
35:
36:
enum BOOL { FALSE, TRUE};
37:
38:
int main()
39:
{
40:
BOOL Stop = FALSE;
// flag for looping
41:
int offset, value;
42:
Array<int> 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 <class T>. That is, change line 30 to read
friend ostream& operator<< (ostream&, Array<T>&);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 <iostream.h>
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 <class T> // 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<T>&);
66:
67:
private:
68:
T *pType;
69:
int itsSize;
70:
};
71:
70:
template <class T>
72:
ostream& operator<< (ostream& output, const Array<T>&
theArray)
73:
{
74:
for (int i = 0; i<theArray.GetSize(); i++)
75:
output << "[" << i << "] " << theArray[i] << endl;
76:
return output;77:
}
78:
79:
void IntFillFunction(Array<int>& theArray);
80:
void AnimalFillFunction(Array<Animal>& theArray);
81:
enum BOOL {FALSE, TRUE};
82:
84:
int main()
85:
{
86:
Array<int> intArray;
87:
Array<Animal> 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<int>& 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<Animal>& theArray)
116:
{
117:
Animal * pAnimal;
118:
for (int i = 0; i<theArray.GetSize(); i++)
119:
{
120:
pAnimal = new Animal;
121:
pAnimal->SetWeight(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 <iostream.h>
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 <class T> // 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<T>&);
private:
T *pType;
int itsSize;
};
template <class T>
Array<T>::Array(int size = DefaultSize):
itsSize(size)
{
pType = new T[size];
for (int i = 0; i<size; i++)
pType[i] = (T)0;
}
template <class T>
Array<T>& Array<T>::operator=(const Array &rhs)
{
if (this == &rhs)
return *this;
delete [] pType;
itsSize = rhs.GetSize();
pType = new T[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
return *this;
}
template <class T>
Array<T>::Array(const Array &rhs)
{
itsSize = rhs.GetSize();
pType = new T[itsSize];100:
for (int i = 0; i<itsSize; i++)
101:
pType[i] = rhs[i];
102:
}
103:
104:
105:
template <class T>
106:
ostream& operator<< (ostream& output, const Array<T>&
theArray)
107:
{
108:
for (int i = 0; i<theArray.GetSize(); i++)
109:
output << "[" << i << "] " << theArray[i] << endl;
110:
return output;
111:
}
112:
113:
114:
Array<Animal>::Array(int AnimalArraySize):
115:
itsSize(AnimalArraySize)
116:
{
117:
pType = new Animal[AnimalArraySize];
118:
}
119:
120:
121:
void IntFillFunction(Array<int>& theArray);
122:
void AnimalFillFunction(Array<Animal>& theArray);
123:
enum BOOL {FALSE, TRUE};
124:
125:
int main()
126:
{
127:
Array<int> intArray;
128:
Array<Animal> 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<int>& 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<Animal>& theArray)
157:
{
158:
Animal * pAnimal;
159:
for (int i = 0; i<theArray.GetSize(); i++)
160:
{
161:
pAnimal = new Animal(i*10);
162:
theArray[i] = *pAnimal;
163:
delete pAnimal;
164:
}
165: }
NOTE: Line numbers have been added to the output to make analysis easier. Line
numbers will not appear in your output.
Output: 1: animal() animal() animal() Enter an offset (0-9) and a
value. (-1 to
stop): 0 0
2: Enter an offset (0-9) and a value. (-1 to stop): 1 1
3: Enter an offset (0-9) and a value. (-1 to stop): 2 2
4: Enter an offset (0-9) and a value. (-1 to stop): 3 3
5: Enter an offset (0-9) and a value. (-1 to stop): -1 -1
6: animal(int) Destroyed an animal...animal(int) Destroyed an
animal...animal(int) Destroyed an animal...initArray...
7: [0] 0
8: [1] 1
9: [2] 2
10:
11: animal array...
12: [0] 0
13: [1] 10
14: [2] 20
15:16: Destroyed an animal...Destroyed an animal...Destroyed an
animal...
17:
<<< Second run >>>
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 <iostream.h>
2:
3:
template <class T> // 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<T>&);
private:
T *pType;
int itsSize;
static int itsNumberArrays;
};
template <class T>
int Array<T>::itsNumberArrays = 0;
template <class T>
Array<T>::Array(int size = DefaultSize):
itsSize(size)
{
pType = new T[size];
for (int i = 0; i<size; i++)
pType[i] = (T)0;
itsNumberArrays++;
}
template <class T>
Array<T>& Array<T>::operator=(const Array &rhs)
{
if (this == &rhs)
return *this;
delete [] pType;
itsSize = rhs.GetSize();
pType = new T[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
}
template <class T>
Array<T>::Array(const Array &rhs)
{
itsSize = rhs.GetSize();
pType = new T[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];62:
itsNumberArrays++;
63:
}
64:
65:
66:
template <class T>
67:
ostream& operator<< (ostream& output, const Array<T>&
theArray)
68:
{
69:
for (int i = 0; i<theArray.GetSize(); i++)
70:
output << "[" << i << "] " << theArray[i] << endl;
71:
return output;
72:
}
73:
74:
75:
Array<Animal>::Array(int AnimalArraySize):
76:
itsSize(AnimalArraySize)
77:
{
78:
pType = new T[AnimalArraySize];
79:
itsNumberArrays++;
80:
}
81:
82:
int main()
83:
{
84:
85:
cout << Array<int>::GetNumberArrays() << " integer
arrays\n";
86:
cout << Array<Animal>::GetNumberArrays();
87
cout << " animal arrays\n\n";
88:
Array<int> intArray;
89:
Array<Animal> animalArray;
90:
91:
cout << intArray.GetNumberArrays() << " integer
arrays\n";
92:
cout << animalArray.GetNumberArrays();
93:
cout << " animal arrays\n\n";
93:
94:
Array<int> *pIntArray = new Array<int>;
95:
96:
cout << Array<int>::GetNumberArrays() << " integer
arrays\n";
97:
cout << Array<Animal>::GetNumberArrays();
98:
cout << " animal arrays\n\n";
98:
99:
delete pIntArray;
100:
101:
cout << Array<int>::GetNumberArrays() << " integerarrays\n";
102:
cout << Array<Animal>::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<int> should match iterator<int>, but not iterator<Animal>.
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> 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 <iostream.h>
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<size; i++)
pType[i] = 0;
}
Array& Array::operator=(const Array &rhs)
{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:
theArray)
80:
81:
82:
83:
84:
85:
if (this == &rhs)
return *this;
delete [] pType;
itsSize = rhs.GetitsSize();
pType = new int[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
return *this;
}
Array::Array(const Array &rhs)
{
itsSize = rhs.GetitsSize();
pType = new int[itsSize];
for (int i = 0; i<itsSize; i++)
pType[i] = rhs[i];
}
int& Array::operator[](int offSet)
{
int size = GetitsSize();
if (offSet >= 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<theArray.GetitsSize(); i++)
output << "[" << i << "] " << theArray[i] << endl;
return output;
}86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103: }
int main()
{
Array intArray(20);
try
{
for (int j = 0; j< 100; j++)
{
intArray[j] = j;
cout << "intArray[" << j << "] okay..." << endl;
}
}
catch (Array::xBoundary)
{
cout << "Unable to process your input!\n";
}
cout << "Done.\n";
return 0;
Output: intArray[0] okay...
intArray[1] okay...
intArray[2] okay...
intArray[3] okay...
intArray[4] okay...
intArray[5] okay...
intArray[6] okay...
intArray[7] okay...
intArray[8] okay...
intArray[9] okay...
intArray[10] okay...
intArray[11] okay...
intArray[12] okay...
intArray[13] okay...
intArray[14] okay...
intArray[15] okay...
intArray[16] okay...
intArray[17] okay...
intArray[18] okay...
intArray[19] okay...
Unable to process your input!
Done.
Analysis: Listing 20.1 presents a somewhat stripped-down Array class, based on the template
developed on Day 19, "Templates." On line 23, a new class is contained within the declaration of the
boundary.This new class is not in any way distinguished as an exception class. It is just a class like any other.
This particular class is incredibly simple: It has no data and no methods. Nonetheless, it is a valid
class in every way.
In fact, it is incorrect to say it has no methods, because the compiler automatically assigns it a default
constructor, destructor, copy constructor, and the copy operator (operator equals); so it actually has
four class functions, but no data.
Note that declaring it from within Array serves only to couple the two classes together. As discussed
on Day 15, "Advanced Inheritance," Array has no special access to xBoundary, nor does
xBoundary have preferential access to the members of Array.
On lines 60-66 and 69-75, the offset operators are modified to examine the offset requested and, if it is
out of range, to throw the xBoundary class as an exception. The parentheses are required to
distinguish between this call to the xBoundary constructor and the use of an enumerated constant.
Note that Microsoft requires that you provide a return statement to match the declaration (in this
case, returning an integer reference), even though if an exception is thrown on line 65 the code will
never reach line 66. This is a compiler bug, proving only that even Microsoft finds this stuff difficult
and confusing!
On line 89, the keyword try begins a try block that ends on line 96. Within that try block, 100
integers are added to the array that was declared on line 88.
On line 97, the catch block to catch xBoundary exceptions is declared.
In the driver program on lines 86-103, a try block is created in which each member of the array is
initialized. When j (line 91) is incremented to 20, the member at offset 20 is accessed. This causes the
test on line 63 to fail, and operator[] raises an xBoundary exception on line 65.
Program control switches to the catch block on line 97, and the exception is caught or handled by
the catch on the same line, which prints an error message. Program flow drops through to the end of
the catch block on line 100.
try Blocks
A try block is a set of statements that begins with the word try, is followed by an opening brace,
and ends with a closing brace. Example:
try
{
Function();
};
catch BlocksA catch block is a series of statements, each of which begins with the word catch, followed by an
exception type in parentheses, followed by an opening brace, and ending with a closing brace.
Example:
try
{
Function();
};
catch (OutOfMemory)
{
// take action
}
Using try Blocks and catch Blocks
Figuring out where to put your try blocks is non-trivial: It is not always obvious which actions might
raise an exception. The next question is where to catch the exception. It may be that you'll want to
throw all memory exceptions where the memory is allocated, but you'll want to catch the exceptions
high in the program, where you deal with the user interface.
When trying to determine try block locations, look to where you allocate memory or use resources.
Other things to look for are out-of-bounds errors, illegal input, and so forth.
Catching Exceptions
Here's how it works: when an exception is thrown, the call stack is examined. The call stack is the list
of function calls created when one part of the program invokes another function.
The call stack tracks the execution path. If main() calls the function
Animal::GetFavoriteFood(), and GetFavoriteFood() calls
Animal::LookupPreferences(), which in turn calls fstream::operator>>(), 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 <iostream.h>
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<size; i++)
pType[i] = 0;
}73:
74:
int main()
75:
{
76:
77:
try
78:
{
79:
Array intArray(0);
80:
for (int j = 0; j< 100; j++)
81:
{
82:
intArray[j] = j;
83:
cout << "intArray[" << j << "] okay...\n";
84:
}
85:
}
86:
catch (Array::xBoundary)
87:
{
88:
cout << "Unable to process your input!\n";
89:
}
90:
catch (Array::xTooBig)
91:
{
92:
cout << "This array is too big...\n";
93:
}
94:
catch (Array::xTooSmall)
95:
{
96:
cout << "This array is too small...\n";
97:
}
98:
catch (Array::xZero)
99:
{
100:
cout << "You asked for an array";
101:
cout << " of zero objects!\n";
102:
}
103:
catch (...)
104:
{
105:
cout << "Something went wrong!\n";
106:
}
107:
cout << "Done.\n";
108:
return 0;
109: }
Output: You asked for an array of zero objects!
Done.
Analysis: Four new classes are created in lines 24-27: xTooBig, xTooSmall, xZero, and
xNegative. In the constructor, on lines 55-70, the size passed to the constructor is examined. If it's
too big, too small, negative, or zero, an exception is thrown.
The try block is changed to include catch statements for each condition other than negative, which
is caught by the "catch everything" statement catch(...), shown on line 103.Try this with a number of values for the size of the array. Then try putting in -5. You might have
expected xNegative to be called, but the order of the tests in the constructor prevented this: size
< 10 was evaluated before size < 1. To fix this, swap lines 60 and 61 with lines 64 and 65 and
recompile.
Exception Hierarchies
Exceptions are classes, and as such they can be derived from. It may be advantageous to create a class
xSize, and to derive from it xZero, xTooSmall, xTooBig, and xNegative. Thus, some
functions might just catch xSize errors, while other functions might catch the specific type of
xSize error. Listing 20.3 illustrates this idea.
Listing 20.3. Class hierarchies and 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:
#include <iostream.h>
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<size; i++)
pType[i] = 0;
}
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
}
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 <iostream.h>
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<size; i++)
pType[i] = 0;
}
int& Array::operator[] (int offSet)
{
int size = GetitsSize();
if (offSet >= 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 <iostream.h>
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<size; i++)
pType[i] = 0;
}
int& Array::operator[] (int offSet)
{
int size = GetitsSize();
if (offSet >= 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 <iostream.h>
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 T>
class Array
{
public:
// constructors
Array(int itsSize = DefaultSize);
Array(const Array &rhs);
~Array() { delete [] pType;}
// operators
Array& operator=(const Array<T>&);
T& operator[](int offSet);
const T& operator[](int offSet) const;
// accessors
int GetitsSize() const { return itsSize; }
// friend function
friend ostream& operator<< (ostream&, const Array<T>&);
// define the exception classes
class xSize {};
private:
int *pType;
int itsSize;
};
template <class T>
Array<T>::Array(int size):
itsSize(size)
{
if (size <10 || size > 30000)
throw xSize();
pType = new T[size];
for (int i = 0; i<size; i++)
pType[i] = 0;
}
template <class T>
Array<T>& Array<T>::operator=(const Array<T> &rhs)
{
if (this == &rhs)
return *this;50:
delete [] pType;
51:
itsSize = rhs.GetitsSize();
52:
pType = new T[itsSize];
53:
for (int i = 0; i<itsSize; i++)
54:
pType[i] = rhs[i];
55:
}
56:
template <class T>
57:
Array<T>::Array(const Array<T> &rhs)
58:
{
59:
itsSize = rhs.GetitsSize();
60:
pType = new T[itsSize];
61:
for (int i = 0; i<itsSize; i++)
62:
pType[i] = rhs[i];
63:
}
64:
65:
template <class T>
66:
T& Array<T>::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 <class T>
76:
const T& Array<T>::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 <class T>
85:
ostream& operator<< (ostream& output, const Array<T>&
theArray)
86:
{
87:
for (int i = 0; i<theArray.GetitsSize(); i++)
88:
output << "[" << i << "] " << theArray[i] << endl;
89:
return output;
90:
}
91:
92:
93:
int main()
94:
{95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116: }
try
{
Array<int> 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<int>::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<int>::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 <iostream.h> 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 <iostream.h>
#include <string.h>
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 <iostream.h>
#include <string.h>
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 <iostream.h>
#include <string.h>
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 <iostream.h>
#include <string.h>
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 <time.h>
#include <iostream.h>4:
int main()
5:
{
6:
time_t currentTime;
7:
8:
// get and print the current time
9:
time (&currentTime); // fill now with the current time
10:
cout << "It is now " << ctime(&currentTime) << endl;
11:
12:
struct tm * ptm= localtime(&currentTime);
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 <stdlib.h>
#include <iostream.h>
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 <iostream.h>
4:
#include <stdlib.h>
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<TableSize; i++)
17:
{
18:
cout << "Enter a number: ";
19:
cin >> 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 <iostream.h>
#include <string.h>
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 <iostream.h>
// 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 T>
class List;
// **************** Node ************
// Generic node, can be added to a list
// ************************************
template <class T>
class Node
{
public:
friend class List<T>;
Node (T*);
~Node();
void SetNext(Node * node) { itsNext = node; }
Node * GetNext() const;
T * GetObject() const;
private:
T* itsObject;
Node * itsNext;
};
// Node Implementations...
template <class T>
Node<T>::Node(T* pOjbect):
itsObject(pOjbect),
itsNext(0)
{}
template <class T>
Node<T>::~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 <class T>
Node<T> * Node<T>::GetNext() const
{
return itsNext;
}
template <class T>
T * Node<T>::GetObject() const
{
if (itsObject)
return itsObject;
else
throw NullNode();
}
// **************** List ************
// Generic list template
// Works with any numbered object
// ***********************************
template <class T>
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<T> * pHead;
int
itsCount;
};
// Implementations for Lists...
template <class T>
List<T>::List():
pHead(0),
itsCount(0)
{}
template <class T>
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<T>::~List()
{
delete pHead;
}
template <class T>
T*
List<T>::GetFirst() const
{
if (pHead)
return pHead->itsObject;
else
throw EmptyList();
}
template <class T>
T * List<T>::operator[](int offSet) const
{
Node<T>* pNode = pHead;
if (!pHead)
throw EmptyList();
if (offSet > itsCount)
throw BoundsError();
for (int i=0;i<offSet; i++)
pNode = pNode->itsNext;
return
}
pNode->itsObject;
// find a given object in list based on its unique number
template <class T>
T*
List<T>::Find(int & position, int ObjectNumber) const
{
Node<T> * 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 <class T>
void List<T>::Insert(T* pObject)
{
Node<T> * pNode = new Node<T>(pObject);
Node<T> * pCurrent = pHead;
Node<T> * 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<Part> 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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
#include <string.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <iostream.h>
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 <fstream.h>
enum BOOL { FALSE, TRUE };
int main(int argc, char**argv)
{
// returns 1 on error6:
7:
if (argc != 2)
8:
{
9:
cout << "Usage: argv[0] <infile>\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 <fstream.h>
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 <iostream.h>
#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 <iostream.h>
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 <iostream.h>
#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<t>::SomeFunction(), also create Array<int>::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 Type>
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 <class Type>
List<Type>::~List()
{
ListCell *pt = head;
while ( pt )
{
ListCell *tmp = pt;
pt = pt->next;
delete tmp;
}
head = tail = 0;
}
template <class Type>
void List<Type>::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 <class Type>
void List<Type>::append( Type value )
{
ListCell *pt = new ListCell( value );
if ( head == 0 )
head = pt;
else
tail->next = pt;
tail = pt;
theCount++;
}
template <class Type>
int List<Type>::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> string_list;
List<Cat> Cat_List;
List<int> 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> 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 <class Type>
int List<Type>::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 <class Type>
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 <iostream.h>
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 <iostream.h>
#include <stdio.h>
#include <string.h>
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 <iostream.h>
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 <iostream.h>
// 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 <iostream.h>
#include <string.h>
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 <iostream.h>
#include <time.h>
int main()
{
time_t currentTime;
struct tm *timeStruct;
time (&currentTime);
timeStruct = localtime(&currentTime);
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 <iostream.h>
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 <ctype.h>
#include <iostream.h>
#include <string.h>
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 <string.h>
#include <iostream.h>
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 <iostream.h>
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 << " = " << add(a,b) << endl;
}
return 0;
}Alternatively, you can solve this problem with recursion:
#include <iostream.h>
unsigned int add( unsigned int lhs, unsigned int rhs )
{
unsigned int carry = lhs & rhs;
unsigned int result = lhs ^ rhs;
if ( carry )
return add( result, carry << 1 );
else
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 << " = " << add(a,b) << endl;
}
return 0;
}