This document discusses C++ memory models and linked data structures. It provides examples of creating struct instances by direct instantiation and dynamic instantiation using pointers. It demonstrates copying and passing struct instances, and discusses issues that can arise from aliasing pointers to struct instances. The document also comments on C++ standards, compilers, and open source politics regarding common C++ compilers like g++ and clang++.
This document discusses C++ memory models and linked data structures. It provides examples of creating struct instances by direct instantiation and dynamic instantiation using pointers. It demonstrates copying and passing struct instances, and discusses issues that can arise from aliasing pointers to struct instances. The document also comments on C++ standards, compilers, and open source politics regarding common C++ compilers like g++ and clang++.
llnked sLrucLures, and a few more Au1s CS138 - WlnLer 2014 rof. Mlke Codfrey
2014-leb-22 "Nice paper, Mike, but could you please make a few editorial changes!
On page 1, !.." 2014-leb-22 This is LaTeX, a document markup language that we researchers use to write our papers in. It is like HTML but much more complicated. Like, C++ complicated. 2014-leb-22 ?ak shavlng Coal: LdlL paper Lo make a few changes & submlL Lo publlsher 8uL new lapLop runs Maverlcks and old lnsLall of La1ex now broken Ck, leL's re-lnsLall, oh no, package managemenL sysLem MacorLs no longer supporLed 8rowse alLernauves, declde Lo lnsLall "homebrew" 8ead homebrew docs, lookup whlch packages l need for La1ex plus any oLhers l mlghL wanL, check whaL packages l had on old lapLop (who dld l glve my old lapLop Lo?) uownload all source les for homebrew lLself, lnsLall ruby, verlfy, complle, lnsLall homebrew package managemenL sysLem uownload Lhe source packages for homebrew LhaL l wanL, lncludlng La1ex, complle, lnsLall, verlfy [1hls Lakes hours!] Ck, back ln buslness now 2014-leb-22 2014-leb-22 1he orlglnal meme [From an old Ren and Stimpy cartoon] 2014-leb-22 2014-leb-22 Cperaung sysLem / hardware Memory (RAM) 8un-ume envlronmenL user's program user's daLa and worklng sLorage SLauc daLa rogram sLack lreesLore (heap) 1he C/C++ (!ava, yLhon, eLc) memory model 2014-leb-22 1he memory model ln C/C++, we Lyplcally complle Lhe source code lnLo Lhe local CS / hardware-speclc language Also, Lhe 81L may be wholly or parLly embedded ln Lhe complled code (sLauc versus dynamlc llnklng) ln !ava and C#, we complle source code lnLo a unlversal vlrLual machlne (vM) language (!vM/CL8) 1he vM ls Lhen Lhe run-ume sysLem, and musL be lmplemenLed Lo run on varlous CSs / hardware plauorms Some languages, llke Scheme, yLhon, and erl are lotetpteteJ 1he run-ume envlronmenL LranslaLes and Lhen execuLes Lhe source code on Lhe y 2014-leb-22 Creaung llnked sLrucLures ln C++ We'd llke Lo creaLe llsL-llke sLrucLures ln C++ We saw how Lo do Lhls ln C uslng C-sLrucLs and polnLers ln CS137 C++ sLrucLs are slmllar buL also dlerenL Loo ln facL, C++ sLrucLs are acLually qulLe a blL more powerful Lhan C sLrucLs 1hey are equlvalenL (lnLerchangeable modulo defaulL vlslblllLy) Lo C++ classes We wlll only llghLly brush on Lhe more powerful sLu noLe LhaL Lhe C++ llbrary denes several useful conLalners (vecLor, llsL, map, eLc), buL we're golng Lo roll our own" 2014-leb-22 Creaung struct/class lnsLances lnsLances of classes/sLrucLs are called objects, Lhere are Lwo ways Lo creaLe Lhem ln C++:
1. ulrecL lnsLanuauon strctName s; Space ls allocaLed on Lhe run-ume sLack 8uL lnsLances dlsappear aL Lhe end of Lhelr denlng scope 1hls ls a problem lf we wanL Lo pass around llnked sLrucLures use a perlod Lo selecL eld (or meLhod) Coord c; c.x = 10; c.y = 20; 2014-leb-22 Creaung struct/class lnsLances 2. uynamlc lnsLanuauon (usually, Lhrough a polnLer) strctName* sPtr= new strctName; Space ls allocaLed on Lhe heap (Lho pLr sPtr ls on Lhe sLack) and perslsLs unul expllclLly deleLed by programmer MusL remember Lo delete lnsLances when no longer needed use an "arrow" (mlnus-greaLer1han) Lo selecL eld/meLhod Coord* p = new Coord; p->x = 10; p->y = 20;
delete p; 2014-leb-22 #include<string> #include<iostream> using namespace std;
struct Coord { int x, y; }; // Need ; at end of struct!
void print (Coord c) { cout << x = << c.x << y = << c.y << endl; } 2014-leb-22 int main (int argc, char* argv[]) { Coord a; a.x = 3; a.y = 5; Coord b = a; // copy! print (a); print (b); a.x = 17; print (a); print (b);
Coord* p3 = &a; // p3 is an alias p3->y = 83; print (a);
delete p1; // good! delete p2; // error delete p3; // error return 0; } 2014-leb-22 [See dlagram] LecLure 3 CS138 W14 2014-leb-22 A word abouL C++ sLandards Cver Lhe years, Lhere have been several omclal sLandard verslons of Lhe C++ language, each wlLh lLs own formal denluon: A8M-90, C++98, C++03, C++11, C++14 (ln dlscusslon phase) ulerences lnclude changes Lo Lhe core language plus new elemenLs ln Lhe C++ sLandard llbrary 1he 8oosL llbrary pro[ecL (www.boosL.org) ls used by Lhe C++ communlLy Lo eld LesL new llbrarles, can oen nd hlgh-quallLy, robusL lmplemenLauons of useful ldeas Lhere 2014-leb-22 A word abouL C++ sLandards l am uslng (mosLly) C++03 ln Lhese noLes. Why? Compllers are only now beglnnlng Lo supporL Lhe C++11 dlalecL fully MosL LexLbooks, web pages, eLc. are sull C++03 for now, Lho Lhls ls changlng Most C++ code you see out there or are ||ke|y to work on |n the near future |s C++03 Companles are generally very slow Lo adopL radlcally new verslons of exlsung Lechnologles: lL's rlsky and expenslve, and Lhe beneLs Lo mlgraung are noL usually worLh Lhe boLher.
2014-leb-22 A word abouL C++ compllers 1he orlglnal A1&1 unlx command-llne compller was called slmply "c++", Lhere are many oLher commerclal compllers ouL Lhere also, lncludlng MS-vlsual SLudlo g++ ls an open source C++ compller from Lhe CCC pro[ecL lL ls "maLure", hlgh quallLy, and supporLs all of C++11 clang++ (whlch slLs on Lop of Lhe LLvM back end) ls anoLher open source C++ compller LhaL ls overLaklng g++ ln popularlLy LLVM/clang++ ls much newer and lLs deslgn ls more modern clang++ ls also hlgh quallLy, and lmplemenLs mosL lf noL all of C++11 clang++ seems Lo be fasLer Lo complle, produce fasLer ob[ecL code, and glve nlcer error messages Lhan g++ 2014-leb-22 Cpen source pollucs g++/CCC ls enurely "free soware", lL ls released under Lhe 3 rd verslon of Lhe Cnu ubllc Llcense (CL3) CL3 requlres LhaL all "derlved works" be released under Lhe CL3 Loo CCC was deslgned Lo be hard Lo break lnLo chunks LhaL could be used as a parL of a non-CL pro[ecL clang++/LLvM ls also "open source", buL Lhe llcense ls dlerenL and more "permlsslve" of whaL you can do wlLh Lhe LLvM codebase 8MS would say LhaL lL ls less "free" LLvM was deslgned Lo be easy Lo Lake aparL + bulld new Lools around Apple abandoned CCC for LLvM largely because of Lhe llcenslng, Lhey are blg conLrlbuLors Lo Lhe LLvM pro[ecL 2014-leb-22 Compller archlLecLure 101 lL's common Lo deslgn a compller wlLh an expllclL "fronL-end" and "back-end" 1he ftoot-eoJ LranslaLes source code lnLo a seL of lnLermedlaLe daLa sLrucLures LhaL are (usually) largely lndependenL of boLh Lhe source language (e.g., C++) and Lhe LargeL plauorm (e.g., Llnux, MacCS uarwln, Wln64) 1he bock-eoJ Lakes Lhe lnLermedlaLe daLa sLrucLures and generaLes blnary execuLables (and oLher blnarles) LhaL are ued Lo Lhe LargeL CS e.g., g++ ls Lhe C++ fronL-end for CCC clang++ ls Lhe C++ fronL-end for LLvM vlsual-C++ ls Lhe C++ fronL-end (+luL) for Mlcroso vlsual SLudlo
jMote oboot compllets comloq lo c5241!] 2014-leb-22 Compller archlLecLure 101 Cen, Lhe fronL- and back-ends can be Leased aparL lnLo separaLe componenLs 1hls allows fronL-ends for dlerenL source languages Lo use a common back-end 8oLh Lhe CCC and LLvM compller sysLems supporL muluple source languages ln Lhls way: e.g., C, C++, Cb[ecuve-C, !ava, Ada, lC818An, 2014-leb-22 g++ CCC backend C++ source C source gcc Wln64 exe Llnux exe C++ compllers ln CS138 lor now, we are uslng g++ wlLh MarmoseL, so LhaL's whaL you have Lo alm Lo geL worklng robably, lf lL works ln clang++, lL wlll work ln g++ Loo 1he uW sLudenL Llnux envlronmenL has boLh g++ and clang++ ?ou can use any compller you llke Lo develop your code, lncludlng MS-vlsual C++ 8u1 lL has Lo work ln MarmoseL uslng g++, and LhaL ls up Lo you Lo ensure 2014-leb-22 #include <string> #include <iostream> using namespace std;
struct Coord { int x, y; }; // Need ";" at end of struct!
2014-leb-22 [See dlagram] C++ polnLers A polnLer ls acLually a (sLrange) number lL's Lhe (numerlc) address of Lhe Lhlng LhaL you are polnung Lo, oen ln Lhe heap 1here ls such a Lhlng as "pLr arlLhmeuc", as you saw ln CS137 1here ls a speclal C++11 value called nullptr lf a pLr has Lhe value nullptr Lhls means, "lm oot polouoq to ooytbloq cotteotly" lf you [usL declare a pLr, lL wlll conLaln random garbage as lLs lnlual value So, aer declarlng a pLr, lL's usually besL Lo seL lL Lo polnL Lo someLhlng or seL lL Lo nullpLr rlghL away [revlously, C++03 used Lhe NULL macro] 2014-leb-22 C++ polnLers . can polnL Lo anyLhlng: struct/class lnsLances (aka objects) strings (whlch are ob[ecLs) ints, doubles, bools, . even oLher pLrs We wlll use Lhem mosLly Lo polnL Lo struct / class lnsLances, Lho lor mosL of our examples, we'll assume we're managlng a llsL of sLrlngs 2014-leb-22 nullptr vs. NULL ln Lhe old days of C, glvlng Lhe (lnLeger) value zero Lo a pLr was used Lo lndlcaLe LhaL Lhe pLr wasn'L polnung Lo anyLhlng LaLer, Lhe macro NULL was creaLed as somewhaL safer and more lnLuluve way Lo express Lhe same ldea 1he (old) C++ sLandards requlres NULL Lo evaluaLe Lo zero, mosLly Lhls worked Ck buL Lhere were some slLuauons ln whlch lL dldn'L . because lL could be LreaLed as an lnLeger Loo C++11 denes Lhe speclal consLanL nullptr lL's a keywotJ ln Lhe C++11 language (can'L reuse lL as a varlable name) lL can be LreaLed as any klnd of pLr, buL noL as an lnLeger lL can also be LreaLed as a bool 2014-leb-22 nullptr vs. NULL ?our compller may noL supporL nullptr as lL's parL of Lhe new C++11 sLandard 8uL our verslon of g++ seems Lo be ne wlLh lL NULL and nullptr should work lnLerchangeably for our purposes ln CS138 . buL use nullptr lf you can ! . and please forglve me lf l forgeL and say NULL !
2014-leb-22 #include <iostream> #include <string> using namespace std;
void swap1 (int x, int y){ const int temp = x; x = y; y = temp; cout << x << y <<endl; }
int main (int argc, char* argv[]) { int x = 5; int y = 37; swap1 (x, y); cout << x << y << endl; return 0; }
2014-leb-22 [See dlagram] #include <iostream> #include <string> using namespace std;
void swap1 (int x, int y){ const int temp = x; x = y; y = temp; cout << x << y <<endl; } void swap2 (int* px, int* py){ const int temp = *px; *px = *py; *py = temp; cout << *px << *py <<endl; }
int main (int argc, char* argv[]) { int x = 5; int y = 37; swap1 (x, y); cout << x << y << endl; swap2 (x, y); cout << x << y << endl; return 0; }
2014-leb-22 [See dlagram] See anything wrong here? #include <iostream> #include <string> using namespace std;
void swap1 (int x, int y){ const int temp = x; x = y; y = temp; cout << x << y <<endl; } void swap2 (int* px, int* py){ const int temp = *px; *px = *py; *py = temp; cout << *px << *py <<endl; }
int main (int argc, char* argv[]) { int x = 5; int y = 37; swap1 (x, y); cout << x << y << endl; swap2 (&x, &y); cout << x << y << endl; return 0; }
2014-leb-22 [See dlagram] #include <iostream> #include <string> using namespace std;
void swap1 (int x, int y){ const int temp = x; x = y; y = temp; cout << x << y <<endl; } void swap2 (int* px, int* py){ const int temp = *px; *px = *py; *py = temp; cout << *px << *py <<endl; }
int main (int argc, char* argv[]) { int x = 5; int y = 37; swap1 (x, y); cout << x << y << endl; swap2 (&x, &y); cout << x << y << endl; int* px, py;//what's py? px = &x; px = new int; *px = 93; delete px; return 0; }
2014-leb-22 [See dlagram] 8evlew: SLauc and dynamlc memory allocauon Memory for your varlables come from elLher: 1. koo-ume stock handles auLomaLed allocauon of sLorage for params and local varlables as procedures are called 1hls sLorage dlsappears auLomaucally aL Lhe end of Lhe proc call 2. lteestote (aka tbe beop) handles all programmauc requesLs for sLorage vla malloc (and relauves) and new (for C++ ob[ecLs/sLrucLs, buL also ints eLc.) user musL reLurn Lhls sLorage when done vla free / delete olnLer p ls on Lhe sLack, Lhe ob[ecL lL polnLs Lo ls on Lhe heap Balloon *p = new Balloon; 2014-leb-22 [See dlagram] #include <iostream> #include <string> using namespace std;
struct Node { string val; Node* next; };
int main (int argc, char* argv[]) { Node* p = new Node; p->val = "first"; p->next = nullptr; Node* q, r; // Note: r NOT a ptr
r.val = "flurble"; q = new Node; q->next = p; q->val = "second"; Node* s; s = new Node; s->val = "third"; s->next = q; Node* temp = s; while (temp != nullptr) { cout << temp->val << endl; temp = temp->next; } // Should delete objects return 0; }
2014-leb-22 [See dlagram] #include <iostream> #include <string> using namespace std;
struct Node { string val; Node* next; };
int main (int argc, char* argv[]) { Node* p = new Node; p->val = "first"; p->next = nullptr; Node* q, r; // Note: r NOT a ptr
r.val = "flurble"; q = new Node; q->next = p; q->val = "second"; Node* s; s = new Node; s->val = "third"; s->next = q; Node* temp = s; while (nullptr != temp) { cout << temp->val << endl; temp = temp->next; } // Should delete objects return 0; }
2014-leb-22 noLe LhaL we don'L really need loLs of sLandalone polnLers p, q, s, eLc. Lo creaLe a llsL lf we wanL Lo creaLe a llsL, all we need ls a pLr Lo Lhe rsL elemenL, Lhen leL each node's next varlable polnL Lo Lhe nexL elemenL We'll use Lhe convenuon LhaL Lhe lasL elemenL's next ls Lhe nullptr (Lhen we can Lell when we're aL Lhe end!) We wlll now go lnLo Au1s, whlch we wlll (someumes) lmplemenL uslng llnked sLrucLures 2014-leb-22 LecLure 6 CS138 W14 2014-leb-22 Arrrggghhh: NULL vs. nullptr NULL works for any dlalecL of C++ nullptr requlres LhaL Lhe compller supporL C++11 1o geL lL Lo work wlLh g++, you may have Lo do Lhls g++ -std=c++0x file.cc lor g++ v4.6.x or g++ -std=c++11 file.cc lor g++ v4.7 or laLer lor our purposes, you can use elLher NULL or nullptr, lL doesn'L really mauer LvenLually, one day, you wlll use nullptr ! 2014-leb-22 Au1s An obsttoct Joto type (Au1) ls a maLhemaucal sLrucLure LhaL has well-dened and recognlzable behavlour lL cootolos Joto LhaL may be accessed only ln a prescrlbed manner, by a seL of named opetouoos Lach operauon has A slqootote, whlch descrlbes Lhe params + Lhe Lype of Lhe reLurned value A pte-cooJluoo (loglc sLmL) LhaL specles whaL ls assumed Lo be Lrue before Lhe operauon may be applled A post-cooJluoo (loglc sLmL) LhaL descrlbes Lhe value / eecL of Lhe operauon 1he formallLy/rlgour of Lhe descrlpuons vary ! 2014-leb-22 Some examples of Au1s A vectot / sepoeoce ls an otJeteJ daLa conLalner LhaL allows random access Lo lndlvldual elemenLs. A stock ls an otJeteJ daLa conLalner wlLh a lllO (lasL ln => rsL ouL) pollcy on lnserLs/deleLes 8uL no random access Lo arblLrary elemenLs A poeoe ls an . lllO . 2014-leb-22 Some examples of Au1s A set ls an oootJeteJ daLa conLalner LhaL can conLaln a glven elemenL aL mosL once A molu-set ls . zero, one, or more umes A Jlcuoooty / mop ls an oootJeteJ daLa conLalner of palrs lf (a,b) and (a,c) are ln M, Lhen b==c *** Can do lookup M[a] reLurns b l (a,b) ln M A molu-mop ls llke a map, excepL LhaL *** ls noL assumed 1he C++ SLandard Llbrary has lmplemenLauons of Lhese and many more besldes! (and so does !ava, C#, .) And Boost.org provldes even more cumng-edge C++ llbrarles 2014-leb-22 Au1s are obsttoct 1here may be muluple ways Lo lmplemenL a glven Au1, buL Lhe absLracL meooloq ls Lhe same regardless 1haL ls, Lhe absLracL speclcauon of, e.g., a sLack, ls Lhe same 1hls polnL ls lmporLanL! Cen, a programmlng language wlll supply means Lo creaLe a hard lotetfoce around Lhe Au1 An lnLerface enforces a llmlLed access of cllenLs Lo Lhe lnLernal deLalls, whlch Lhe cllenLs should noL be Louchlng dlrecLly C++/!ava/C# do Lhls very well vla class denluons (C noL so much) 2014-leb-22 WhaL klnd of daLa do Au1s hold? Many, buL noL all, Au1s are prlmarlly daLa cootoloets 1hey keep a collecuon of daLa of a parucular klnd organlzed ln some lnLeresung way e.g., sLack, queue, Lree, vecLor, . uaLa conLalners can conLaln [usL abouL any klnd of daLa! lor now, we'll assume LhaL elemenLs are sLrlngs, cos Lhey're more fun Lo play wlLh When we geL lnLo CC, we wlll show you how Lo dene qeoetlc conLalners LhaL can hold any klnd of elemenL klnd 2014-leb-22 SLyle of Au1s lnlually, we are golng Lo use a foocuoool programmlng sLyle Lo creaLe our Au1s ln C-lsh C++ 1hls ls clean maLhemaucally, buL someumes lnemclenL lf done nalvely due Lo needless parameLer copylng 1hen, we'll use a more C-llke procedural model uslng tefeteoce potometets llnally, we'll lmplemenL Lhem ln an ob[ecL-orlenLed (CC) sLyle 1he general formaL of an Au1 operauon ln Lhls sLyle ls: oewAu1 opNome (olJAu1, otbetlotoms) l.e., pass ln Lhe Au1 as a param, geL a new one as a resulL for muLaLor ops 2014-leb-22 Return value 1he sLack Au1 A !"#$% ls an ordered conLalner of daLa LhaL enforces a lllO pollcy on adds/removes ermlued operauons: push, pop, peek, isEmpty . plus a "consLrucLor" (cxr), whlch we'll call initStack for now nlLs: Someumes, pop and peek are comblned lnLo one operauon LhaL performs boLh ops (and ls called pop) Someumes, peek ls called top 2014-leb-22 1he sLack Au1: More formally initStack (no args) creaLes + reLurns a (new) empLy sLack Lo Lhe caller push Lakes sLack s and an elemenL e oew and reLurns a new sLack LhaL's ldenucal Lo s excepL wlLh Lhe new elemenL on "Lop": e oew e 1 . e N
pop Lakes a sLack s and reLurns a new sLack LhaL's ldenucal Lo s excepL wlLh Lhe Lop elemenL removed: e 2 . e N
peek Lakes a sLack and reLurns (buL does noL remove) Lhe rsL elL: e 1 isEmpty Lakes a sLack and reLurns Lrue or false, dependlng lf Lhe sLack ls empLy: l.e., does N=0? [Assume s ls a sLack whose currenL sequence ls e 1 . e N and e oew ls Lhe new elemenL
]
2014-leb-22 re- and posL-condluons 1hese are sLaLemenLs of loglc LhaL lnvolve Lhe parameLers, Lhe reLurn value, and (maybe) Lhe global program sLaLe re-condluon ls Lhere anyLhlng we need (assume) Lo be Lrue abouL Lhe parameLers (+ maybe Lhe global sLaLe) before uslng Lhls operauon? osL-condluon WhaL ls Lhe relauonshlp beLween Lhe lnpuL params + (evenLual) reLurn value (+ maybe Lhe global sLaLe)? l.e., WhaL does Lhls operauon acLually do? 2014-leb-22 initStack : -> sLack re: Lrue osL: reLurned value ls a (new) empLy sLack
isEmpty : sLack -> boolean re: Lrue osL: reLurn value ls Lhe same as n==0
push: sLack x elemenL -> sLack re: Lrue osL: reLurned sLack ls e oew e 1 . e N
1he sLack Au1: Lven more formally [Assume s ls a sLack whose currenL sequence ls e 1 . e N and e oew ls Lhe new elemenL
]
2014-leb-22 pop : sLack -> sLack re: !isEmpty osL: reLurned sLack ls e 2 . e N
peek : sLack -> elemenL re: !isEmpty osL: reLurned value ls e 1
nuke : sLack -> sLack re: true osL: reLurned value ls an empLy sLack, old nodes deleLed
1he sLack Au1: Lven more formally [Assume s ls a sLack whose currenL sequence ls e 1 . e N and e oew ls Lhe new elemenL
]
2014-leb-22 // This is roughly what we want, eventually // It make take a while to get there // but how should we define the Stack type itself?
int main (int argc, char* argv[]) { Stack s; s = initStack (); s = push (s, "alice"); s = push (s, "bob"); cout << peek (s) << endl; s = pop (s);
}
2014-leb-22 SLack lnLerface vs. lmplemenLauon 1hls ls Lhe lotetfoce for a sLack LhaL wlll be Lhe same (ldeally) for any reasonable lmplemenLauon ln a C-llke sLyle llrsL, we'll look aL an lmplemenLauon LhaL uses a llnked llsL 1hen we wlll use a vector-based approach 8uL Lhe absLracL lnLerface should be Lhe same! Agaln, for now we assume an elemenL Lype of string Ceoetlc sLacks (comlng laLer) wlll Lake any klnd of elemenL, llke a vector does 2014-leb-22 lmplemenung a sLack as a llnked llsL llrsL lnLeresung deslgn quesuon: WhaL daLa Lype (parameLer/reLurn Lype) should we use Lo model a sLack as a llnked llsL? 8ecall our llnked llsL from Lhe oLher day: We don'L need a pLr varlable on Lhe sLack for each elemenL, lnsLead we keep a pLr Lo only Lhe rsL elemenL, and Lhen every Node lnsLance (on Lhe heap) has a pLr Lo Lhe next elemenL So we [usL have Lo keep Lrack of a slngle Node* So Lhe daLa Lype LhaL besL models a sLack wlLh Lhls approach ls Node* 2014-leb-22 lmplemenung a sLack as a llnked llsL Conslder:
void push (Node* first, string val) { Node* newNode = new Node; newNode->val = val; newNode->next = first; // now what? first = newNode; }
2014-leb-22 lmplemenung a sLack as a llnked llsL Conslder Lhls slgnaLure for push void push (Node* first, string val) lf we wanL Lo push a new elemenL on Lo Lhe fronL of Lhe llsL, Lhen Lhe first pLr wlll need Lo be reseL Lo polnL Lo Lhe new Node we creaLe And lf we change Lhe first parameLer lnslde Lhe body of push, Lhe eecL wlll noL percolaLe back Lo Lhe caller " osslble soluuons: use tefeteoce potometets (laLer) uene a class wlLh meLhods push, pop, eLc. (laLer) 8eLurn Lhe new value of first as Lhe value of push Node* push (Node* first, string val) 2014-leb-22 lmplemenung a sLack as a llnked llsL Node* push (Node* first, string val) { Node* newNode = new Node; newNode->val = val; newNode->next = first; // now what? This! return newNode; }
// in the main program Node* s = nullptr; // new empty stack // pass a stack in, get a new stack back s = push (s, "alice"); s = push (s, "bob");
2014-leb-22 lmplemenung a sLack as a llnked llsL now leL's lmplemenL pop, peek, and isEmpty, . and an "exLra" operauon nuke . LhaL cleans up any mess we made when we're done wlLh Lhe sLack l.e., deleLe Lhe heap-based Nodes we were uslng 2014-leb-22 LecLure 7 CS138 W14 2014-leb-22 int main (int argc, char* argv[]) { Node* s = nullptr; s = push (s, "alice"); s = push (s, "bob"); s = push (s, "carol"); cout << peek(s) << endl; s = pop(s); cout << peek(s) << endl; s = nuke(s); return 0; }
2014-leb-22 [See dlagram] ldlng Lhe lmplemenLauon . a blL mm, we are tepteseouoq llsLs by uslng a polnLer Lo Lhe rsL elemenL lL's klnd of ugly for cllenLs Lo have Lo say Node* when Lhey are Lhlnklng 5tock Also lL's ugly for Lhem Lo have Lo know Lhe lnluallzauon deLalls So leL's do Lhree Lhlngs: 1. Add typedef Node* Stack; aL Lhe beglnnlng of Lhe program 2. 8eplace Node* by Stack ln Lhe param llsLs 3. CreaLe an expllclL new funcuon initStack 2014-leb-22 #include<string> #include<iostream> #include<cassert> using namespace std;
} 2014-leb-22 [See dlagram] Why Joo't we geL copy semanucs? 1he C-llke programmlng model provldes only weak supporL for programmer-dened absLracuons ln Lhls deslgn, we're [usL renamlng a polnLer Lype wlLh a fancy name, C doesn'L glve us Lhe power of real CC e.g., denlng how new ob[ecLs are made, (re)denlng whaL asslgnmenL means We need C++-sLyle structs / classes Lo do Lhe [ob properly, as we'll see 2014-leb-22 SLack lnLerface vs. lmplemenLauon 1ake a good look aL Lhe lnLerface of Lhe varlous sLack operauons, and lgnore Lhe code for a mlnuLe lL's posslble Lo undersLand how Lo use a sLack by slmply sLudylng Lhe lnLerface (lf Lhere's enough deLall specled by commenLs) 1he acLual deLalls of Lhe lmplemenLauon don'L mauer Lo Lhe cllenL much as long as Lhey do Lhe [ob correcLly We wlll see how we mlghL lmplemenL a sLack dlerenLly whlle sull uslng Lhe same lnLerface LaLer we wlll use an CC sLyle Lo change Lhe synLax sllghLly buL preservlng Lhe ldeas So on Lo Lhe vector-based approach! And, yes, l wlll Lalk abouL emclency and param copylng aerwards 2014-leb-22 // Exactly the same main program as w. linked list int main (int argc, char* argv[]) { // Note that we can have two distinct // stacks at the same time! Stack s1; // except that no initStack is required Stack s2; s1 = push (s1, "alpaca"); s1 = push (s1, "beaver"); s1 = push (s1, "cat"); s1 = push (s1, "dog"); s2 = push (s2, "one"); s2 = push (s2, "two"); cout << peek(s1) << endl; cout << peek(s2) << endl; s1 = pop(s1); cout << peek(s1) << endl; nuke(s1); nuke (s2); return 0; } 2014-leb-22 [See dlagram] ComplexlLy of sLack operauons Llnked llsL lmplemenLauon push, pop, peek, nuke vecLor lmplemenLauon push, pop, peek, nuke 2014-leb-22 SL Leachable momenL: AdapLers ?ou mlghL say Lo yourself: ney, tbe vector closs Joes tbe job fot me. wby Joot l jost ose tbot Jltectly lo my molo ptoqtom losteoJ of botbetloq wltb Jefoloq o speclol lotetfoce ooJ coJe tbot boslcolly jost teJltects foocuoo colls 1he answer ls LhaL when you deslgn an Au1 for use by oLhers, Lhe lnLerface ls Lhe mosL lmporLanL parL!
2014-leb-22 // Using a vector directly instead of a // Stack adapter class int main (int argc, char* argv[]) { vector<string> s1; s1.push_back("alpha"); s1.push_back("beta"); s1.push_back("gamma"); vector<string> s2; string v = s1.back(); cout << v << endl; s1.pop_back(); cout << s1.back() << endl; s2.push_back(v); cout << s2.back() << endl; s2.pop_back(); cout << s2.back() << endl; // Assertion failure return 0; } 2014-leb-22 An adapLer 1he sLack-lmplemenLed-by-a-vecLor ls an example of whaL's called an oJoptet 1he sLack Au1 has a small, well undersLood Al: push, pop, eLc. 8uL vector has loLs of oLher funcuonallLy sLacks don'L need e.g., random access Lo elemenLs, lLeraLors, capacity ueslgnlng an lnLerface LhaL provldes exoctly whaL cllenLs need and no more prevenLs Lhe cllenL from dependlng on feaLures Lhey shouldn'L and also proLecLs Lhem from posslble fuLure changes ln Lhe underlylng lmplemenLauon 2014-leb-22 An adapLer lf we dlscover a more emclenL way of lmplemenung Lhe sLack lnLerface, cllenLs don'L need Lo change Lhelr code as long as Lhe lnLerface doesn'L change Loo e.g., suppose we found LhaL Lhe vecLor class ran slowly on cerLaln klnds of machlnes or LhaL Lhere was a llcenslng problem wlLh Lhe code 1hls sounds llke 8&u deslgn, buL lL's perhaps Lhe slngle mosL lmporLanL sw englneerlng deslgn prlnclple Lhere ls: lofotmouoo blJloq: SeparaLe lnu from lmpl. lde lmpl deLalls. ave cllenLs depend only on well-deslgned, unllkely-Lo-change lnLerfaces. 1he lnu should be much less llkely Lo change Lhan Lhe lmpl deLalls. 2014-leb-22 Should we used a llnked llsL or a vecLor? Why? Well LhaL's a good quesuon, ln facL, Lhere already ls a generlc Stack class ln Lhe S1L! lL's an adapLor based on a deque (whlch ls somewhaL llke a vector), and ln a sLyle very slmllar Lo whaL we dld (buL wlLhouL Lhe egreglous copylng, more laLer), so really we should choose LhaL lf Lhere were no such llbrary class, Lhen l would probably choose a vector-based lmplemenLauon over Lhe homebrew llnked llsL, as Lhe S1L vector class ls ln wlde use and does Lhe [ob and ls llkely Lo be slmpler Lo lmplemenL (pLrs + llnked sLrucLures => bugs for a whlle) 2014-leb-22 Should we used a llnked llsL or a vecLor? Why? 1he general polnL, Lho, ls LhaL you don'L and shouldn'L need Lo undersLand how someLhlng ls lmplemenLed ln order Lo use lL eecuvely And lmplemenLers of llbrary classes wlll usually Lry Lo hlde Lhelr non- essenual lmpl deLalls from you, and you should be graLeful for Lhls And when you deslgn classes for use wlLhln a sw sysLem, Lhlnk abouL whaL are Lhe essenual, public parLs, and whlch should be hldden away from cllenLs as private 2014-leb-22 Cne more emclency lssue 1he nave funcuonal-sLyle vecLor-based approach we have used does a LC1 of copylng of vecLors! lL ls noL aL all reallsuc for emclency, Lho lL ls maLhemaucally Ck A reallsuc lmplemenLauon ln a funcuonal sLyle someumes requlre Lrlcks Lo make Lhlngs emclenL, buL we won'L go Lhere lnsLead, we wlll (laLer) show you how Lo do Lhls ln a sLaLe-based CC sLyle LhaL ls ne, emclency-wlse 1he llnked llsL approach we used does noL have Lhls problem 2014-leb-22 8eference parameLers C /!ava supporL call-by-value" (only) for parameLers Copy value of parameLer onLo call stock ftome (aka ocuvouoo tecotJ) 1hls has real overhead cosL lf Lhe param ls a "blg" enuLy, llke an ob[ecL LhaL conLalns many sub-ob[ecLs Cnly Lhe reLurn value (lf any) ls copled back Can change param values lnslde proc, buL changes do noL propagaLe back Lo Lhe calllng envlronmenL Can use pLr params Lo cheaL" (common C pracuce, we wlll avold Lhls) Changes Lo pLr don'L propagaLe back, buL changes Lo values polnLed Lo do! Can use pLrs Lo pLrs Lo change pLrs! Wouldn'L lL be nlce Lo allow changes Lo parameLers Lo propagaLe back Lo Lhe calllng envlronmenL, lf/when we wanL? 2014-leb-22 8eference parameLers C++ also supporLs Lhe ldea of tefeteoce parameLers uL an ampersand aer Lhe parameLer Lype ln a proc decl
1he param ls oot copled onLo Lhe call sLack Any changes you make ln procedure wlll propagaLe back Lo Lhe caller 1hls ls called coll-by-tefeteoce 1he param acLs klnda llke a pLr, buL you use lL normally" as a varlable of LhaL Lype, noL by dereferenclng wlLh * 8ef params are Lhe norm ln C++, geL used Lo Lhem Can have a ref or const ref tetoto type . buL walL for CS247 2014-leb-22 AnoLher name for a reference ls an allas. AnoLher name for "anoLher name for" ls allas. "AnoLher name for "AnoLher name for"" ls a ulne. Are ulnes allases? ulscuss for 10 marks.
2014-leb-22 #include <iostream> #include <string> #include <cassert> using namespace std; void swap1 (int x, int y){ const int temp = x; x = y; y = temp; cout << x << y <<endl; } void swap2 (int* px, int* py){ const int temp = *px; *px = *py; *py = temp; cout << *px<< *py <<endl; }
void swap3 (int& x, int& y){ const int temp = x; x = y; y = temp; } int main (int argc, char* argv[]) { int x = 5; int y = 37; swap1 (x, y); cout << x << y << endl; swap2 (&x, &y); cout << x << y << endl; swap3 (x, y); cout << x << y << endl; return 0; }
2014-leb-22 LecLure 8 CS138 W14 2014-leb-22 CS138 Mld-Lerm . ls not durlng mld-Lerm week, sorry folks . lL's on 1hursday, 13 lebruary l.e., Lwo weeks from Loday
?eah, ylkes, l mean l sull have Lo come up wlLh lL LasL lecLure of LesLable maLerlal: 1hursday, 6 lebruary 2014-leb-22 #include <iostream> #include <string> #include <cassert> using namespace std; void swap1 (int x, int y){ const int temp = x; x = y; y = temp; cout << x << y <<endl; } void swap2 (int* px, int* py){ const int temp = *px; *px = *py; *py = temp; cout << *px<< *py <<endl; }
void swap3 (int& x, int& y){ const int temp = x; x = y; y = temp; } int main (int argc, char* argv[]) { int x = 5; int y = 37; swap1 (x, y); cout << x << y << endl; swap2 (&x, &y); cout << x << y << endl; swap3 (x, y); cout << x << y << endl; return 0; }
2014-leb-22 8eference parameLers C++ supporLs tefeteoce parameLers uL an ampersand aer Lhe parameLer Lype ln a proc decl
1he param ls oot copled onLo Lhe call sLack Any changes you make ln procedure wlll propagaLe back Lo Lhe caller 1hls ls called coll-by-tefeteoce 1he param acLs klnda llke a pLr, buL you use lL normally" as a varlable of LhaL Lype, noL by dereferenclng wlLh * 8ef params are Lhe norm ln C++, geL used Lo Lhem Can have a ref or const ref tetoto type . buL walL for CS247 2014-leb-22 const& parameLers A const ref param ls a ref param LhaL you can'L change 1he compller wlll prevenL you from changlng Lhe ob[ecL lnslde Lhe procedure Semanucally, lL's olmost Lhe same as a value param lL ls legal (buL mosLly useless) Lo change a value param const refs are also more emclenL Lhan value params, as you don'L copy Lhe whole ob[ecL onLo Lhe run ume sLack, [usL a ref 1hls ls Lhe preferred way ln C++ Lo declare params LhaL you don'L lnLend Lo change lnslde Lhe procedure 2014-leb-22 "8elax, l've only had Lwo beers" uslng a (non-const) reference parameLer ls llke glvlng away Lhe keys Lo your car aL a fraL parLy !usL how much do you LrusL LhaL procedure, anyway? Cen, you really do need Lo do lL, buL evaluaLe Lhe rlsk use a const ref lf Lhe param should noL be changed by Lhe procedure
2014-leb-22 8eference params vs. pLrs 1he C language does noL have references, lL requlres uslng polnLer params Lo make changes "percolaLe back" Many C++ sysLems sLarLed ouL llfe as a C sysLem (and/or belng developed by C programmers), so you wlll oen see Lhls sLyle (pLr params) ln lndusLrlal C++ code 1he beuer, cleaner, easler-Lo-undersLand C++ approach ls Lo use reference (and consL ref) parameLers So C++ ob[ecLs should osoolly be passed by reference, noL value or by polnLer 1ho l oen "forgeL" for sLrlngs & slmllar small ob[ecLs and pass Lhem by value Ck Lo use value params for "small Lhlngs" (baslc Lypes / scalars) llke int, double, bool, eLc, assumlng you don'L need changes Lo percolaLe back And lf you do, you can always use a reference param for Lhem Loo 2014-leb-22 typedef Node* Stack;
string peek1 (Stack & first) { string ans = first-> val; first = nullptr; return ans; }
string peek2 (Stack first) { string ans = first-> val; first = nullptr; return ans; }
string peek3 (const Stack & first) { string ans = first-> val; first = nullptr; return ans; }
2014-leb-22 legal and nasLy Lo caller lllegal, compller wlll complaln legal buL has no eecL on caller // struct Employee { string name; int monthlySalary; // other stuff too, probably };
// red text means common C++ usage void GiveRaise1 (Employee e, int raise) {} void GiveRaise2 (const Employee e, const int raise) {} void GiveRaise3 (Employee &e, int &raise) {} void GiveRaise4 (const Employee &e, ) {} void GiveRaise5 (Employee *e, ) {} void GiveRaise6 (const Employee *e, ) {} void GiveRaise7 (Employee *const e, ) {} void GiveRaise8 (const Employee *const e, ) {}
int main () { Employee * pFrank = new Employee; pFrank -> name = "Frank Lee"; pFrank -> monthlySalary = 5000;
// int franksRaise = 700; GiveRaise1 (*pFrank, franksRaise); // } 2014-leb-22 void GiveRaise1 (Employee e, int raise) {} When Lhls fcn ls called, a copy of Lhe caller's ob[ecL/struct lnsLance e and lnLeger raise are creaLed on sLack. 1hese coples may be changed wlLhln Lhe fcn, buL Lhe values do noL percolaLe back Lo caller and are losL aL end of meLhod call. (CC full LruLh: Lhe meLhod for maklng a copy ls dened by Lhe Employee copy consLrucLor, we'll see Lhls ln CS247)
void GiveRaise2 (const Employee e, const int raise){} A copy of ob[ecL e and lnLeger raise are creaLed on sLack, Lhey may noL be changed wlLhln Lhe fcn/meLhod. Lecuvely, Lhls ls preuy slmllar Lo GiveRaise1 2014-leb-22 void GiveRaise3 (Employee &e, int &raise) {} Any menuon of ob[ecL e or lnLeger raise lnslde Lhe meLhod acLually refers Lo Lhe varlables ln Lhe caller's envlronmenL. 1hus, changes can be made Lo raise or e lnslde GiveRaise3 percolaLe back Lo Lhe calllng envlronmenL lmmedlaLely. void GiveRaise4 (const Employee &e, ) {} 8ead Lhls as "e ls a ref Lo a Lhlng LhaL's a const Employee" Any menuon of e refers Lo caller's envlronmenL, buL you can'L change Lhe ob[ecL e refers Lo ln any way. Lecuvely, Lhls ls slmllar Lo GiveRaise2 excepL LhaL lL may be more emclenL lf Employee ls a large" ob[ecL.
2014-leb-22 void GiveRaise5 (Employee *e, ) {} Call w/o dereferenclng: GiveRaise5(pFrank,) [same for 6, 7, 8] 8ead Lhls as "e ls a polnLer Lo an Employee" [more C-sLyle Lhan C++] A copy of a polnLer ls made on Lhe sLack, lf you manage Lo change Lhe member varlables of Lhe ob[ecL e polnLs Lo, Lhese changes wlll perslsL ln Lhe calllng envlronmenL ?ou can also make e Lo polnL Lo a new or dlerenL ob[ecL, buL LhaL change wlll be losL aL Lhe end of Lhe meLhod call, and noL percolaLe back void GiveRaise6 (const Employee *e,) {} 8ead Lhls as "e ls a polnLer Lo a Lhlng LhaL's a const Employee" 1haL ls, e can be changed Lo polnL Lo a dlerenL const Employee, buL any lnsLance lL polnLs Lo cannoL be changed lnLernally. lf you do change e Lo polnL Lo anoLher ob[ecL, Lhls ls noL percolaLed back Lo Lhe caller. 2014-leb-22 void GiveRaise7 (Employee *const e, ) {} 8ead Lhls as "e ls a const polnLer Lo an Employee" ?ou can change Lhe lnLernal values of Lhe ob[ecL e polnLs Lo, buL you can'L make e polnL Lo a dlerenL ob[ecL 1he const doesn'L add much, as wlLh GiveRaise2 void GiveRaise8 (const Employee *const e, ) {} 8ead Lhls as "e ls a const polnLer Lo a const Employee" Can'L change Lhe value of Lhe ob[ecL e polnLs Lo, can'L change e Lo polnL Lo anoLher ob[ecL A sLrong guaranLee for C, buL Clve8alse4 makes Lhe same guaranLee and ls easler Lo undersLand 2014-leb-22 usually, use Lhe sLyle of GiveRaise3 lf you woot changes Lo percolaLe back Lo Lhe calllng envlronmenL, or GiveRaise4 lf you Joo't woot changes Lo percolaLe back . buL uslng value params as ln GiveRaise1 ls also common 8eference parameLers are your frlend! refer Lhem over C-sLyle polnLers for parameLers LhaL are struct / class lnsLances 2014-leb-22 8eferences vs. reference parameLers ln C++, normal ldenuers (varlables) can be references Loo, buL generally we use Lhem only Lo glve a more convenlenL name Lo someLhlng long .
// Reasonable use const Employee & e = emplList[Wloo].find(empNum); cout << e.getName() << " " << e.getAddr() <<endl;
// Probably useless/bad idea int x = 13; int& y = x; y = 36; // Changes value of x! So C++ reference parameLers are [usL parameLers LhaL happen Lo be references, lf LhaL makes sense Lo you lf noL, don'L sweaL lL Loo much 2014-leb-22 8eference params and Au1s We can now easlly swlLch Lo reference params ln our Au1 denluons Cld sLyle: funcuonal and sLaLeless l.e., pass ln old Au1, geL new one back as reLurn value of proc nave lmplemenLauon may resulL ln loLs of unneeded copylng of large ob[ecLs, dependlng on lmpl chosen new sLyle: sLaLeful. ass ln "car keys" of Lhe Au1 lnsLance Lo procs, changes may be made Lo lLs lnLernals before belng passed back (unless passed as a const reference) no egreglously unnecessary copylng 2014-leb-22 // vector-based approach of Stack is now quite reasonable // No more crazy egregious copying of param vectors! #include <iostream> #include <string> #include <vector> #include <cassert> using namespace std;
// Simple impl using a vector; as before typedef vector<string> Stack;
// Note that we're not going to implement // initStack in this version
2014-leb-22 // New way with ref params bool isEmpty (const Stack& s){ return s.size()==0; }
void push (Stack& s, string e){ s.push_back(e); }
void pop (Stack& s) { assert (!isEmpty(s)); s.pop_back(); } 2014-leb-22 // Old way with value params bool isEmpty (Stack s) { return s.size()==0; }
// New way with ref params string peek (const Stack& s){ assert (!isEmpty(s)); return s.back(); }
void nuke (Stack& s) { while (!isEmpty(s)) { pop(s); } } // Old way with value params string peek (Stack s) { assert (!isEmpty(s)); return s.back(); }
void nuke (Stack s) { while (!isEmpty(s)) { s = s.pop(); } }
2014-leb-22 // Main program as w. linked list w. ref params int main (int argc, char* argv[]) { // Note that we can have two distinct // stacks at the same time! Stack s1; Stack s2; push (s1, "alpaca"); // Old: s1 = push (s1, "alpaca"); push (s1, "beaver"); // etc. push (s1, "cat"); push (s1, "dog"); push (s2, "one"); push (s2, "two"); cout << peek(s1) << endl; // Same as non-ref param vers cout << peek(s2) << endl; pop(s1); // Old: s1 = pop(s1); cout << peek(s1) << endl; nuke(s); // Same as non-ref param vers return 0; } 2014-leb-22 1he 3 varlable klnds you meeL ln C++ Clobol votlobles are dened ootslJe of any encloslng funcuon/class/sLrucL 1hey come lnLo exlsLence when declared and dle aL Lhe end of Lhe program We haven'L really used Lhem ln CS138, Lhelr use ls generally frowned upon locol votlobles (lncl. params) are dened wltblo a funcuon/meLhod body 1hey come lnLo exlsLence (evenLually) when Lhe funcuon ls called, and dle when lL LermlnaLes (lf noL before) Membet / lostooce votlobles are a sob-pott of a larger varlable LhaL ls an lnsLance of a struct or class 1hey are born when Lhe lnsLance ls creaLed, and dle when Lhe lnsLance dles
[We lgnore static class varlables and oLher mlnor excepuons for now] 2014-leb-22 Scopes of ldenuers and A8s ln C/C++, an lJeoufet (usually, a varlable) ls vlslble from lLs declarauon unul Lhe end of Lhe currenL scope": Lnd of currenL block Lnd of a procedure body Lnd of loop / if / switch body, eLc. Clobal varlables are vlslble globally (unul end of le scope) A scope ls a (usually) conuguous chunk of a program LhaL dellneaLes boundarles of vlslblllLy for ldenuers 8efore Lhe 1999 sLandard, all declarauons ln C had Lo come aL Lhe beglnnlng of a scope (noL Lrue for C++) 2014-leb-22 Scopes of ldenuers and A8s As your program execuLes, scopes are enLered and exlLed When a new scope ls enLered, an ocuvouoo tecotJ (A8, area of sLorage also called a stock ftome) ls creaLed for lL A8s conLaln sLorage for parameLers, Lhe (evenLual) reLurn value, and local varlables LuS Lhe locauon of where Lhe call was made from Someumes Lhe same fcn can be called ln muluple places wlLhln a fcn When a varlable declarauon ls encounLered, space for LhaL varlable ls creaLed ln Lhe currenL A8 2014-leb-22 Scopes of ldenuers and A8s "OotslJe of o Joq, o book ls o moo's best ftleoJ. loslJe of o Joq, lt's too Jotk to teoJ." Croucho Marx on scopes
When Lhe currenL scope ls exlLed, Lhe A8 for lL ls desLroyed, along wlLh Lhe sLorage for Lhe varlables ConLrol reLurns Lo Lhe calllng fcn aL Lhe polnL Lhe call was made So you need Lo Lake care where Lhlngs are declared 2014-leb-22 #include <iostream> #include <string> using namespace std;
// Why won't this compile? int max (int x, int y) { if (x > y) { int bigger = x; } else { int bigger = y; } return bigger; } 2014-leb-22 #include <iostream> #include <string> using namespace std;
int max (int x, int y) { // The scope of bigger needs to include // the return statement! int bigger; if (x > y) { bigger = x; } else { bigger = y; } return bigger; } 2014-leb-22 int main (int argc, char* argv[]) { int x, y; cin >> x >> y; if (x == y) { cout << "they're equal" << endl; } else { int ans = max (x, y); cout << "max is " << ans << endl; } // note: ans is not visible from here on // } 2014-leb-22 [See dlagram] 1he run-ume call sLack AL any glven momenL, you mlghL have several scopes open: e.g., main calls f calls g calls h lf you pause Lhe execuuon of a program, Lhe sLack wlll conLaln an acuvauon record for Lhe pendlng call chaln Lach pend|ng ca|| has |ts own Ak (p|us Aks for any |ntra-fcn scopes) When Lhe currenL (l.e., deepesL nesLed) call compleLes, lLs A8 ls deleLed (aer copylng back any reLurn value) LvenLually, all calls wlll nlsh, and Lhelr A8s wlll be deleLed Loo 2014-leb-22 #include <iostream> #include <string> using namespace std; // All of these assume n >= 0 // Mildly flabby pedagogic version int fact1 (int n) { int ans; if (n <= 1) { ans = 1; } else { ans = n * fact1 (n-1); } return ans; }
// Compact, somewhat cryptic version int fact2 (int n){ return n <= 1 ? 1 : n * fact2 (n-1); }
2014-leb-22 // The third way; use this, we will int fact (int n) { if (n <= 1) { return 1; } else { return n * fact(n-1); } }
int main (int argc, char* argv[]) { int k; while ((cin >> k) && k >= 0) { const int kfact = fact(k); // new const each time! cout << k << "! = << kfact << endl; return 0; } }
2014-leb-22 [See dlagram] 8ecurslon ?ou may have nouced LhaL Lhe fact funcuon calls lLself, as you know, Lhls ls called tecotsloo ln CS137, you saw CCu, mergsesorL, qulcksorL, eLc 8ecurslon ls an eleganL and powerful concepL for problem solvlng 8uL lL's noLhlng maglcal Lo lmplemenL, you [usL geL muluple A8s for Lhe same funcuon ln Lhe call sLack aL Lhe same ume. 2014-leb-22 8ecurslon 8ecurslon ls a general Lechnlque for solvlng a large problem, Lyplcally by progresslvely breaklng down Lhe lnpuL unul lL's small enough" Lo solve easlly, Lhen comblnlng Lhe resulLs back LogeLher 1hree baslc parLs: 1. 1rlvlal base case(s) (LhaL can be solved easlly and dlrecLly) 2. 8educuon operaLor (make Lhe daLa smaller") 3. Composluon operaLor (compose Lhe answer Lo Lhe smaller" problem Lo geL Lhe full answer) 2014-leb-22
int fact1 (int n) { if (n <= 1) { return 1; } else { return n * fact1 (n-1); } }
8asls case Composuon 8educuon 2014-leb-22 1owers of anol We'll come back Lo recurslon ls more deLall when we sLudy more Au1s (esp. Lrees) . buL before we go, leL's look aL a classlc example: 1he Lowers of anol Classlc formulauon: ln a Lemple ln anol, Lhere are 64 dlerenLly-slzed dlsks sLacked on a pole ln lncreaslng order of slze 1he monks musL move Lhe dlsks one aL a ume from one pole Lo a second pole, uslng a Lhlrd pole as a Lemporary no larger dlsk may ever slL on Lop of a smaller dlsk 1he soluuon ls really shorL and eleganL! : lf Lhey move one dlsk per second, how long Lo move Lhe whole sLack? 2014-leb-22 #include <iostream> #include <string> #include <cassert> using namespace std; void hanoi (int N, int src, int dest, int temp) { if (N>0) { hanoi (N-1, src, temp, dest); cout << "Move from " << src << " to " << dest << endl; hanoi (N-1, temp, dest, src); } } int main (int argc, char* argv[]) { int N; cout << "How many rings? "; cin >> N; assert (N>0); hanoi (N, 1, 3, 2); return 0; } 2014-leb-22 1he queue Au1 LasL day, we showed how Lhe run-ume sLack sLore A8s of pendlng calls (lncludlng recurslve calls) now, we'll expand Lhls menLal model Lo lnclude Lhe run-ume heap (free sLore) . buL rsL leL's meeL ouL second Au1: Lhe queue A poeoe ls a conLalner of daLa LhaL enforces a lllO pollcy on adds/removes 1he permlued operauons are called enter, leave, first, isEmpty (plus a consLrucLor initQueue) 2014-leb-22 1he queue Au1 More formally: initQueue creaLes + reLurns a new empLy queue enter (enqueue) Lakes an elemenL e new and a queue q and reLurns a new queue wlLh Lhe new elemenL aL Lhe end: e 1 . e n e new
leave (dequeue) Lakes a queue q and reLurns a new queue LhaLs ldenucal Lo q excepL wlLh Lhe rsL elemenL removed: e 2 . e n
first (fronL) Lakes a queue + reLurns (buL doesn'L remove) rsL elemenL: e 1 isEmpty 1akes a queue and reLurns Lrue or false, dependlng lf Lhe queue ls empLy: l.e., does N==0? [lor each, assume q ls a queue whose currenL sequence ls e 1 . e n and e new ls Lhe new elemenL
]
2014-leb-22 1he queue Au1: Lven more formally initQueue: -> queue re: Lrue osL: reLurned value ls a new empLy queue
enter: queue x value -> queue re: Lrue osL: reLurned queue ls e 1 . e n e new
leave : queue -> queue re: ! lsLmpLy osL: reLurned queue ls e 2 . e n
2014-leb-22 first : queue -> value re: ! lsLmpLy osL: reLurned value ls e 1
isEmpty : queue -> boolean re: Lrue osL: reLurn value ls Lhe same as n==0
nuke : queue -> queue re: Lrue osL: reLurn value empLy queue, old nodes deleLed 1he queue Au1: Lven more formally 2014-leb-22 typedef <mumble> Queue // If we were using the truly functional style, we'd do this Queue initQueue() {} bool isEmpty (Queue q) {} Queue enter (Queue q, string val) {} Queue leave (Queue q) {} string first (Queue q) {} Queue nuke (Queue q)
int main (int argc, char* argv[]) { Queue q1; q1 = enter (q1, "early"); q1 = enter (q1, "timely"); q1 = enter (q1, "late"); cout << first(q1) << endl; q1 = leave (q1); cout << first(q1) << endl; q1 = nuke (q1); return 0; }
2014-leb-22 typedef <mumble> Queue // But we're going to use reference parameters instead void initQueue (Queue& q) {} bool isEmpty (const Queue & q) {} void enter (Queue & q, string val) {} void leave (Queue & q) {} string first (const Queue & q) {} void nuke (Queue & q)
int main (int argc, char* argv[]) { Queue q1; enter (q1, "early"); enter (q1, "timely"); enter (q1, "late"); cout << first(q1) << endl; leave (q1); cout << first(q1) << endl; nuke (q1); return 0; }
2014-leb-22 LecLure 9 CS138 W14 Clven by Adam 8oeglesL 2014-leb-22 #include <iostream> #include <string> #include <cassert> using namespace std; void hanoi (int N, int src, int dest, int temp) { if (N>0) { hanoi (N-1, src, temp, dest); cout << "Move from " << src << " to " << dest << endl; hanoi (N-1, temp, dest, src); } } int main (int argc, char* argv[]) { int N; cout << "How many rings? "; cin >> N; assert (N>0); hanoi (N, 1, 3, 2); return 0; } 2014-leb-22 lmplemenung a queue wlLh a vecLor lmplemenung a sLack was easy uslng a vecLor. ow can we lmplemenL a queue? lf we used a vecLor, we'd also have Lo keep Lrack of where Lhe rsL elemenL was Loo nalve vecLor approach: enter calls push_back 8uL where do we sLore first? Can'L make lL a sLandalone global varlable, we we wanL Lo be able Lo creaLe muluple dlsuncL queues 2014-leb-22 lmplemenung a queue wlLh a vecLor Suppose we had a queue LhaL was never more Lhan Lhree elemenLs long aL any glven momenL, buL mlghL have a gazllllon elemenLs ln lLs llfeume 1hls would be really space-lnemclenL (unless we reslzed lL someumes, or used a clrcular array, buL LhaL's less slmple) lnsLead, leL's use a struct wlLh pLrs . ln a mlnuLe 2014-leb-22 lmplemenung a queue as a llnked llsL now leL's (re)conslder Lhe queue 8ecall LhaL nalvely uslng a vecLor (as a value parameLer) Lo lmplemenL a queue mlghL wasLe a loL of space over ume . so leL's roll our own dlrecuons uslng a llnked llsL agaln, buL wlLh a polnLer Lo boLh Lhe rsL and lasL elemenL We'll dene initQueue, isEmpty, enter, leave, first 2014-leb-22 typedef <mumble> Queue
int main (int argc, char* argv[]) { Queue q1; enter (q1, "early"); enter (q1, "timely"); enter (q1, "late"); cout << first(q1) << endl; leave (q1); cout << first(q1) << endl; nuke (q1); return 0; }
2014-leb-22 [See dlagram] We sull wanL Lo dene a nlce absLracL, clean looklng Lype for cllenLs Lo use . . buL we now have Lo keep Lrack of botb a rsL + lasL pLr . whlch means we can'L use Lhe Lrlck of lemng a Node* represenL Lhe Au1 as we dld wlLh Stack / SortedList lnsLead, we need a sLrucLured represenLauon . l.e., a struct ln addluon Lo Lhe Node sLrucL Lype 2014-leb-22 LecLure 10 CS138 W14 Clven by Adam 8oeglesL 2014-leb-22 A phllosophlcal polnL uoes Lhe check ln leave really need Lo be done? no, as long as we leave Lhe resL of Lhe code as-ls lf we used q.last==nullptr for isEmpty, Lhen lL would noL work owever, lL's a llule dangerous Lo break Lhe represenLauonal conslsLency of rsL and lasL belng boLh nuLL or non-nuLL 8euer Lo keep Lhe represenLauon correcL ln case you declde Lo change your mlnd abouL Lhe lmplemenLauon laLer 2014-leb-22 ComplexlLy of queue operauons Llnked llsL lmplemenLauon enter, leave, first, isEmpty, nuke vecLor lmplemenLauon umm, no Lhanks. 2014-leb-22 A noLe on defenslve programmlng When you sLarL Lo fool around wlLh pLrs, lL's easy Lo make oosty mlsLakes e.g., forgeL Lo seL a pLr, seL lL Lo Lhe wrong value ?ou may never nouce Lhe error, or see lL manlfesLed only much laLer on 1wo complemenLary approaches Lo undersLandlng whaL wenL wrong: 1. uslng a debugger (long LuLorlal) 2. lnserung prlnL sLmLs and asseruons (easy) ?ou wlll need Lo learn a debugger evenLually, buL you can go a long way wlLh prlnL sLmLs and asseruons Also, commenL anyLhlng LhaL's non-obvlous. ?ou wlll Lhank yourself laLer on (and your colleagues may Lhank you Loo) 8uL don'L over-commenL 2014-leb-22 A noLe on defenslve programmlng When Lo lnserL a prlnL sLmL ? AL Lhe beglnnlng of a funcuon call (prlnL params) AL Lhe end of a fcn call (prlnL resulLs / sLaLe of world) !usL before deleung someLhlng AL any ma[or declslon polnL Any ume you do someLhlng lnLeresung Cood Lo creaLe speclal purpose prlnL fcns for whole daLa sLrucLure (for debugglng only) e.g., prlnL a whole sLack, prlnL all values of a struct lnsLance rlnung pLr values noL very useful, prlnL Lhe value of Lhe Lhlngs Lhey polnL Lo lnsLead 2014-leb-22 A noLe on defenslve programmlng #include <cassert> Why should we use asseruons, lf Lhey make our program dle? use lL anywhere you wanL Lo be JeoJ sure of some facL before proceedlng, proceedlng lf lL's false ls polnLless 1wo usual cases: 1. Check LhaL your loglc/assumpuons are correcL (up Lo you) lf first ls nullptr you expecL LhaL last wlll be Loo 2. Check LhaL "user" ls belng reasonable (up Lo user, noL you) e.g., popplng an empLy sLack 2014-leb-22 A noLe on defenslve programmlng 8uL clean up when you're done! 8emove "Lype 1" asseruons, dlagnosuc prlnL sLmLs and speclal prlnL funcuons ln Lhe verslon you dellver Ck Lo have Lhem ln durlng developmenL + debugglng, buL when you're ready Lo shlp, geL rld of Lhem "1ype 2" asseruons can someumes be le ln, as long as Lhe check lsn'L very expenslve e.g., checklng lf a pLr ls nullpLr ls cheap, checklng lf a llsL ls sorLed ls expenslve 2014-leb-22 #include <iostream> #include <string> #include <cassert> using namespace std;
// many of these per Q struct Node { string val; Node* next; };
// one of these per Q struct Queue { Node* first; Node* last; };
void leave (Queue & q) { cerr << "leave: "; assert (!isEmptyQ (q)); Node *p = q.first; cerr << p->val << endl; q.first = q.first->next; if (nullptr == q.first) { q.last = nullptr; } delete p; } 2014-leb-22 void nuke (Queue & q) { // cerr << "nuke " << endl; while (!isEmptyQ (q)) { leave(q); } } 2014-leb-22 // A non-standard API element for Queue, // used only for debugging! void printQ (const Queue & q) { // print the values in order // implementation left to student } 2014-leb-22 uynamlc arrays We are golng Lo dlscuss dynamlc arrays" Lo glve you an undersLandlng of whaL's golng on under Lhe hood" owever, lf you feel llke you need a dynamlc array ln your program (lgnorlng Lhe nexL assgL :-), you should almosL cerLalnly use an S1L vector (or relauve) lnsLead. vecLors are usually lmplemenLed uslng a dynamlc array, whlch ls why we are sLudylng Lhem. ln facLs Lhere's noL much need Lo use any klnd of C-sLyle arrays ln C++ slnce we have vecLors, plus oLher powerful conLalner classes ln Lhe S1L. 2014-leb-22 uynamlc arrays 1. SLaucally (as ln C) SLorage ls allocaLed on Lhe sLack Array bound musL be a complle-ume consLanL
int main () { const int N = 5; int A[N]; // legal int m; cin << m; int B[m]; // illegal
} C++ arrays can be declared ln Lwo ways: 2014-leb-22 [See dlagram] uynamlc arrays int main () { int N; cin >> N; int* A = new int[N]; string* B = myAlloc(N); for (int i=0; i<N; i++){ cin >> A[i] >> B[i]; } // do more fun stuff delete [] A; delete [] B; return 0; } 2. Dynamically (C++ only) Storage is allocated on the heap Array bound can be a run- time value (positive int) Must delete when done ! Need []! string* myAlloc (int n) { assert (n>0); return new string[n]; }
2014-leb-22 [See dlagram] uynamlc arrays Can'L reLurn an array from a procedure 8uL arrays are almosL [usL polnLers (wlLh a speclal synLax for accesslng elemenLs, eLc.) So can reLurn a pLr lnsLead, lf we allocaLe Lhe sLorage on Lhe heap 2014-leb-22 uynamlc arrays ow does "delete []A" work? 1he sysLem needs Lo remember how many elemenLs are ln Lhe heap- chunk LhaL was allocaLed for Lhe dynamlc array 1he exLenL of Lhe array ls assoclaLed somehow wlLh Lhe block of sLorage on Lhe heap, oot wlLh Lhe pLr
: Why noL sLore Lhe exLenL aL Lhe beglnnlng of Lhe array? A: 1haL would break normal array addresslng (pLr arlLhmeuc) Common approach ls Lo use, eg, 4 byLes befote Lhe array sLorage Lo hold Lhe exLenL So Lhls means LhaL Lhe followlng works ne 2014-leb-22 #include <iostream> #include <string> #include <cassert> using namespace std; string* myAlloc (int n) { assert (n>0); return new string[n]; } int main (int argc, char* argv[]) { string* A = myAlloc(10); string* B = myAlloc(20); string* temp = A; A = B; // No problem here B = temp; delete []A; // If we stored extent w ptr, this delete []B; // would NOT work, but it does return 0; } 2014-leb-22 [See dlagram] no exLenL for you! 1he exLenL of a dynamlcally allocaLed array has Lo be sLored somewhere . . 8u1 lL ls noL accesslble programmaucally or any oLher way (modulo non-porLable "cheaung") Cnly Lhe compller/run-ume knows where Lo nd lL A dynamlcally allocaLed array ls [usL an array, lL ls noL an ob[ecL and does noL have an Al . so Lhere ls no size() meLhod [We are noL Lalklng abouL Lhe C++11 class called array, whlch ls someLhlng dlerenL] 2014-leb-22 LecLure 11 CS138 W14 2014-leb-22 Mld-Lerm Cn 1hurs, Lhls week, as you know ! 1esLable conLenL lncludes everyLhlng up Lo bot oot locloJloq Loday's lecLure: Sllde conLenLs, sLu l sald/wroLe/drew LhaL may noL be on slldes AsslgnmenL conLenLs (buL noL low-level deLalls) AnyLhlng l sald lsn'L LesLable won'L be LesLed e.g., oJvooceJ unlx maLerlals ln slldes 2014-leb-22 Mld-Lerm MlxLure of quesuon klnds: ShorL answer, Lrue/false WhaL does Lhls program do? WrlLe a program LhaL does xxx WhaL's wrong wlLh Lhls program? uraw a memory dlagram of Lhls polnL ln execuuon 1he lasL (mulu-parL) quesuon ls worLh 1/3 of Lhe LoLal marks . so allocaLe your ume wlsely ln Lhe lmmorLal words of Lhe laLe uouglas Adams . 2014-leb-22 1he llnked llsL 1he generlc "Lhlng" we have been worklng wlLh Lo lmplemenL our sLacks, queues, eLc. ls called a "llnked llsL". lL has A speclal pLr Lo Lhe "rsL" elemenL A bunch of "node" lnsLances, llnked Lo each oLher Lo form a "llne" vla a "nexL" polnLer Llnked llsLs can be unordered, or ordered by lnseruon ume, or ordered by key value, or ordered by some sLrange "oLher" convenuon 2014-leb-22 varlanLs of Lhe llnked llsL uoubly-llnked llsL: Llnk Lo rsL and lasL elemenLs of llsL, can Lraverse forwards or backwards
struct Node { string val; Node* next; Node* prev; }; 2014-leb-22 varlanLs of Lhe llnked llsL 8lnary Lree (more laLer) Speclal "rooL" node Lach node has Lwo "chlld" llnks, a le and a rlghL Can be sorLed (8S1, heap) or noL, dependlng on use non-blnary Lrees also exlsL
struct Node { string val; Node* left; Node* right; }; 2014-leb-22 LlsLs and orderlng So far, our llsLs (sLack, queue) have been ordered by arrlval ume ln some way 1hese are called otJeteJ llsts We can also creaLe llsLs sorLed by Lhe ("key") value of Lhe elemenL (l.e., sorLed by daLa value) 1hese are called sotteJ llsts ?ou can lmaglne LhaL Lhe daLa sLored ln Lhe node ls much rlcher (l.e., more elds) Lhan [usL a slngle sLrlng, buL we'll use only string keys for slmpllclLy 1he prlorlLy queue (comlng soon) ls a hybrld, parLly based on order of arrlval and parLly based on daLa (Lhe prlorlLy) 2014-leb-22 SorLed llnked llsL LeL's use Lhe same Node and creaLe a sotteJ llsL Au1 We need Lo dene: initList, insert, remove, has, isEmpty, print 2014-leb-22 Cemng llnked sLrucLures rlghL Llnked sLrucLures are easy Lo geL wrong! ?ou may noL nouce Lhe lncorrecL llnk unul much laLer, oen lL's hard Lo gure ouL whaL wenL wrong e.g., forgemng Lo seL llnk of lasL elemenL Lo NULL ls noL a problem lf you never go Lo Lhe end agaln 1hlnk defenslvely! Some Lrlcks: SeL polnLers Lo NULL lf Lhey're noL "acuve" CreaLe "sLaLe-reporung funcuons" Lo help ln debugglng use lC and asseruons Lo check your assumpuons (evenLually) Learn how Lo use a debugger. 2014-leb-22 Cemng llnked sLrucLures rlghL Suggesuons: 1. uraw plcLures! (Lven l do Lhls 23 years laLer) 2. 8reak down lnLo all posslble "lnLeresung" cases you can Lhlnk of: LmpLy llsL, one elemenL llsL, "blg" llsL llrsL elemenL, mlddle elL, lasL elL 1hlng noL Lhere, dupllcaLe Lhlng, all elLs same, . See lf you can merge cases LogeLher laLer 3. 1esung, Lesung, Lesung! uon'L rely on MarmoseL Lo Lell you everyLhlng LhaL's wrong, lL's Lhere Lo help buL lL's noL a cruLch uon'L be shy abouL creaung large numbers of LesL cases, lL's whaL Lhe pros do 2014-leb-22 ?ou are your own besL LesLer! A slmple process you can lmplemenL now! 1. ueslgn a seL of LesL cases for your code, glve each a descrlpuve name 1hls mlghL be done wlLh a common drlver (maln) program LhaL can read ln Lhe LesL case from a daLa le, or you may need a new maln program for each 2. lor each LesL case, creaLe a plaln LexL le wlLh Lhe expecLed ouLpuL for each LesL run 3. 8un each LesL, sLore Lhe ouLpuL ln a llke-named le (one per LesL run) 4. use diff Lo compare Lhe expecLed Lo Lhe acLual keqtessloo tesuoq keep your LesL cases around, tetest evetytbloq when you make a change, agaln, Lhls ls whaL Lhe pros do! "A boq ls slmply o test cose yoo fotqot to wtlte." 2014-leb-22 1esL-drlven developmenL 1est-Jtlveo Jevelopmeot ls commonly parL of "aglle developmenL" processes uevelop LesL cases LhaL sausfy Lhe reqs befote you wrlLe Lhe code 1hen wrlLe Lhe slmplesL code LhaL sauses Lhe LesLs keep Lhe LesL cases up Lo daLe! 1he LesLs evolve as your code does. 2014-leb-22 #include <iostream> #include <string> #include <cassert> using namespace std;
struct Node { string val; string otherStuff; // we wont really touch it Node* next; };
void print (const SortedList& first) { cout << "Printing the list:\n"; Node* p = first; while (NULL != p) { cout << " \"" << p->val << "\"" << endl; p = p->next; } } 2014-leb-22 // Let's implement these!
// Typically, nodes store key value plus other info. // Thus lookup would return the "other info" for // that key. Since this is simpler version, we'll // just return a boolean to indicate if we found it. bool lookup (const SortedList & first, string val) {}
// Also ignoring otherStuff from here on in. void insert (SortedList& first, string val) {} void remove (SortedList& first, string val) {}
[8ralnsLorm for cases Lo conslder, lL's Ck lf some overlap] 1. 2. 3. 4. 3. 6. 7. 2014-leb-22 // Final version that combines all cases into // two main scenarios; requires list be sorted already. void insert (SortedList& first, string val) { // cerr << "Inserting " << val << endl; // debug Node* newNode = new Node; newNode->val = val; if (NULL == first || val <= first->val) { // Scenario 1: insert as first element newNode->next = first; first = newNode; } else { // Scenario 2: insert somewhere after first element Node* cur = first; while (NULL!=cur->next && val > cur->next->val){ cur = cur->next; } newNode->next = cur->next; cur->next = newNode; } } 2014-leb-22 8emove void remove (SortedList& first, string val) {} [8ralnsLorm for cases Lo conslder, lL's Ck lf some overlap] 1. 2. 3. 4. 3. 6. 7. 2014-leb-22 // Final version, that combines all cases into // two main scenarios; requires list be sorted already. void remove (SortedList& first, string val) { // cerr << "Deleting \"" << val << "\"\n"; // debug assert (!isEmpty(first)); Node* temp;
if (first->val == val) { // Scenario 1: Deleting first element temp = first; first = first->next;
// cont'd 2014-leb-22 } else { // Scenario 2: Deleting non-first element Node* cur = first; while (cur->next!=NULL && val>cur->next->val) { cur = cur->next; } // My orig 2009 version: NULL==cur (WRONG!) if (NULL == cur->next || val != cur->next->val){ // assert (false); // optional cerr << Couldn't find \"" << val << "\"\n"; return; } temp = cur->next; cur->next = cur->next->next; // aka temp->next } // Common to both scenarios delete temp; } 2014-leb-22 LecLure 12 CS138 W14 2014-leb-22 First- year and graduating students:
Check your email February 13 th
for the National Survey of Student Engagement (NSSE)! IMPROVE THE WATERLOO EXPERIENCE! Tell us about your experience at Waterloo!
Take 15 minutes to earn
$5 on your WatCard and an entry into a draw for
a $500 undergraduate award National Survey of Student Engagement (NSSE) MldLerm Any quesuons abouL Lhe mldLerm? Medlan 77.3 Average 73.7
Check over your mldLerm gradlng Check Lhe addlng Loo "Crade grubblng" ls noL really encouraged, buL lf Lhere's a mlsLake (e.g., your soluuon ls correcL buL noL whaL we expecLed) Lhen Lalk Lo ChanLelle 2014-leb-24 2014-leb-22 http://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c 1eachable momenL: Why you should use for slngle llnes Cver Lhe weekend of 22 leb 2014, Apple announced Lhere was a serlous bug ln Lhelr SSL verlcauon code for lCS 1hls ls a fronL-llne check for maklng secure neLwork connecuons, lL mauers! 8aslcally, lL was noL checklng SSL cerLs ot oll, due Lo a dumb bug LhaL would probably have been avolded lf Lhey had used Lhe convenuon of always uslng for slngle llne lf-sLaLemenL bodles 1he acLual code ls open source . . and Lhe rooL of Lhe problem ls [usL sloppy programmlng sLyle! 2014-leb-22 WhaL happens here? Suppose LhaL (as ls usually Lhe case) Lhe signedParam check passes + err ls seL Lo "all ls well" 1he rsL goto fail ls (rlghLly) sklpped, buL Lhe second ls noL! 1he valldauon of Lhe SSL cerucaLe (err = sslRawVerify()) ls sklpped ConLrol Lransfers Lo Lhe fail code, whlch frees some space & reLurns owever, Lhe error code ls "all ls well" ls reLurned as Lhe value of Lhe funcuon 1he hashout and sslRawVerify checks are never performed, ln facL, all code aer Lhe second goto fail up Lo Lhe fail label ls unreachable (aka "dead") code. 2014-leb-22 lf Lhey had used for slngle llne lf-sLmL bodles, Lhen elLher 1he second goto fail would have been lnslde lL (and never execuLed, and so harmless), C8 lL would have been ouLslde Lhe and more obvlously vlsually wrong 1o me, Lhe mosL amazlng Lhlng ls noL Lhe dumb bug (Lhey happen), buL Lhe facL LhaL Apple's Lesung lnfrasLrucLure lsn'L seL up Lo caLch Lhls, esp. glven Lhls ls fronL-llne securlLy sLu 8eadlng Lhe code glves an obvlous seL of evenLs Lo check for! Ma[or soware englneerlng fall on Lhelr parL! 1hls ls a publlc embarrassmenL for Lhem, leL's see how Lhey respond 2014-leb-22 2014-leb-24 8ack Lo Lhe sorLed llnked llsL ere's insert and remove, whlch we developed lasL ume 2014-leb-22 // Final version that combines all cases into // two main scenarios; requires list be sorted already. void insert (SortedList& first, string val) { // cerr << "Inserting " << val << endl; // debug Node* newNode = new Node; newNode->val = val; if (NULL == first || val <= first->val) { // Scenario 1: insert as first element newNode->next = first; first = newNode; } else { // Scenario 2: insert somewhere after first element Node* cur = first; while (NULL!=cur->next && val > cur->next->val){ cur = cur->next; } newNode->next = cur->next; cur->next = newNode; } } 2014-leb-22 // Final version, that combines all cases into // two main scenarios; requires list be sorted already. void remove (SortedList& first, string val) { // cerr << "Deleting \"" << val << "\"\n"; // debug assert (!isEmpty(first)); Node* temp;
if (first->val == val) { // Scenario 1: Deleting first element temp = first; first = first->next;
// cont'd 2014-leb-22 } else { // Scenario 2: Deleting non-first element Node* cur = first; while (cur->next!=NULL && val>cur->next->val) { cur = cur->next; } // My orig 2009 version: NULL==cur (WRONG!) if (NULL == cur->next || val != cur->next->val){ // assert (false); // optional cerr << Couldn't find \"" << val << "\"\n"; return; } temp = cur->next; cur->next = cur->next->next; // aka temp->next } // Common to both scenarios delete temp; } 2014-leb-22 ueslgnlng LesL cases ln WlnLer 2010, l dlscovered a bug ln Lhe remove code l had wrluen for WlnLer 2009 lL manlfesLed lLself only when you Lrled Lo remove an enLry LhaL wasn'L acLually Lhere Anu was greaLer Lhan Lhe lasL elemenL ln Lhe llsL 8uL lf you never Lrled LhaL case, lL looked correcL. 1hls ls noL aL all uncommon!
2014-leb-22 ueslgnlng LesL cases Lessons learned: Llnked sLrucLures are teolly hard Lo geL 100 correcL lL's common for obscure bugs Lo be dlscovered laLer on, even aer Lhe code has worked preuy well for a long ume 8ugs oen show up only Lhrough systemouc Lesung unless you are an lCS developer ! 1esung ls good for your code: 8e bruLal and creauve uslng debugged llbrarles ls always beuer Lhan re-lnvenung Lhe wheel, you wlll probably geL lL wrong Lhe rsL, second, Lhlrd ume you Lry 2014-leb-22 Some LesL cases for remove LlemenL presenL Cne elemenL llsL 8emove rsL elemenL 8emove mlddle elemenL 8emove lasL elemenL 8emove lasL-buL-one elemenL LlemenL noL presenL no elemenLs (asseruon fallure, we hope) 8efore rsL elemenL 8eLween mlddle elemenLs Aer lasL elemenL LlemenL already deleLed 2014-leb-22 ueslgnlng LesL cases lundamenLal LruLh of sw Lesung: ?ou cannoL LesL "everyLhlng", comblnaLorlal exploslon of posslble worlds hlLs you very qulckly So Lesung becomes an opumlzauon game: Clven xx person-hours, whaL ls Lhe mosL eecuve way Lo deslgn and lmplemenL a Lesung lnfrasLrucLure? lL ls much beuer Lo carefully deslgn a seL of cases LhaL LesL expllclL slLuauons Lhan Lo Lhrow a bunch of quasl-random daLa aL Lhe sysLem ln addluon Lo Lesung agalnsL Lhe absLracL ldea of whaL Lhe procedure ls dolng (block-box tesuoq), Lhere are approaches LhaL LesL agalnsL how Lhe code ls acLually wrluen (wblte-box tesuoq) 1hls ls an advanced Loplc whlch you wlll revlslL laLer WhlLe-box Lesung mlghL have helped Lo caLch my bug 2014-leb-22 lookup (Lo geL Lo Lhe elemenL's correcL place): Average case C(N]2) == C(N) WorsL case C(N) insert, remove: Lecuvely, Lhey requlre a lookup Loo 8uL once you arrlve, |t's an C(1) operanon to ad[ust a few po|nters We menuon Lhls as ln C++ we someumes reLurn lLeraLors LhaL polnL Lo elemenLs ln Lhe mlddle of Lhe llsL 1he C++ S1L conLalner class list ls lmplemenLed as a plaln old doubly-llnked llsL ComplexlLy of sorLed llnked llsL ops 2014-leb-22 . compared Lo arrays Accesslng an array elemenL ls: a consLanL ume operauon! e.g., A[13] == addressCfA + 13 * elemenLSlze 8uL arrays have a xed slze, unllke llnked llsLs, we can'L (easlly) grow Lhem vectors have exLra space aL Lhe end, and can grow as needed lf you were Lo lnserL a new elemenL "ln place": lL's C(n) Lo nd Lhe rlghL spoL (or C(log n) you use blnary search) . . Lhen followed by C(n) copy acuons, whlch ls falrly expenslve 2014-leb-22
enter : sortedList X value -> sortedList re: sortedList ls sorLed osL: sortedList ls sorLed && sortedList conLalns new elemenL && sortedList has same elemenLs as before, plus Lhe new one remove : sortedList X value -> sortedList re: sortedList ls sorLed && lndlcaLed elemenL presenL osL: sortedList ls sorLed && sortedList has same elemenLs as before, mlnus Lhe lndlcaLed one SorLed llnked llsL: re- and posL-condluons 2014-leb-22 1he prlorlLy queue Au1 A ptlotlty poeoe ls llke a queue, buL each elemenL has a value ooJ an lnLeger prlorlLy (usually, >=0) enter ls "same as before" (?ou can'L really Lell whaL happens unul you call leave) leave means "remove Lhe oldesL elemenL from among Lhose wlLh Lhe lowesL (or hlghesL) prlorlLy" Cen used ln neLwork rouung Some klnds of daLa are more lmporLanL Lhan oLhers, Lhey geL preferenual LreaLmenL e.g., oS, medla sLreamlng, vol 2014-leb-22 1he prlorlLy queue Au1 Always have Lo speclfy: uoes hlghesL or lowesL prlorlLy mean "mosL lmporLanL"? noLe LhaL Lhe C++ S1L provldes an lmplemenLauon of priority_queue, whlch ls whaL you should use ln real llfe 8uL leL's Lry lmplemenung lL ourselves . any ldeas? Cne ldea: keep a llsL LhaL ls sorLed by prlorlLy, buL wlLhln a glven prlorlLy, oldesL ls aL Lhe fronL 2014-leb-22 // A working but nave // implementation
struct Node { string val; int prior; Node* next; };
2014-leb-22 // This is the only hard one; nave implementation void enterPQ (PQ& pq, string val, int prior) { Node* p = new Node; p->val = val; p->prior = prior; if (NULL == pq || pq->prior > prior) { p->next = pq; pq = p; } else { // want first node > prior, not >= Node* temp = pq; while (NULL != temp->next && temp->next->prior <= prior) { temp = temp->next; } p->next = temp->next; temp->next = p; } }
2014-leb-22 // Let's trace this thru int main (int argc, char* argv[]) { PQ pq; initPQ (pq); enter (pq, apple, 3); enter (pq, baker, 3); enter (pq, corps, 6); enter (pq, deck, 2); enter (pq, extra, 3); return 0; } 2014-leb-22 ComplexlLy of operauons lor Lhls parucular (nalve) lmplemenLauon only:
firstPQ C(1)
leavePQ C(1)
enterPQ C(N) 2014-leb-23 An even slmpler lmplemenLauon enterPQ: keep a llnked llsL sorLed ln arrlval order (l.e., llke a queue) ComplexlLy: C(1) leavePQ, firstPQ: SLarL aL beglnnlng, look for lowesL prlorlLy elemenL 8uL how do we know whaL Lhe lowesL prlorlLy ls? umm, . ComplexlLy: C(N) [1hls ls preuy awful] 2014-leb-23 : A beuer lmplemenLauon We malnLaln a llsL of llsLs (LCL)! Speclcally, we malnLaln a sorLed llsL of queues 1he (ouLer) llsL ls sorLed numerlcally by prlorlLy 1hls ls whaL you wlll lmplemenL ln an upcomlng assgL! Can re-use code from queue and sorLed llnked llsL! 2014-leb-22 2014-leb-22 : LCL lmplemenLauon string first_PQ (const PQ& pq) pre: !(isEmptyPQ(pq)) osL: 8eLurn Lhe value of rsL elemenL of rsL queue!
ComplexlLy: C(1) 2014-leb-23 : LCL lmplemenLauon void leave_PQ (PQ& pq) pre: !(isEmptyPQ(pq)) osL: erform leaveQ on rsL queue ln LCL lf Lhls queue ls now empLy, mlghL wanL Lo deleLe Lhe PQnode also ComplexlLy: C(1) 2014-leb-23 : LCL lmplemenLauon void enter_PQ (const PQ& pq, string val, int p) llrsL, nd ouL lf Lhere ls an exlsung queue for prlorlLy p lf noL, creaLe a new PQnode, and lnserL lL lnLo Lhe ouLer sorLed llsL ln lLs proper place, creaLe a new (empLy) queue LhaL Lhls node wlll polnL Lo now, add val Lo Lhe queue for prlorlLy p ComplexlLy: [Assumlng n elemenLs ln LoLal, and k dlsuncL prlorlues] SLep 1: llnd approprlaLe queue (lf lL exlsLs, or creaLe one) 1hls ls C(k) SLep 2: Add new elemenL aL end of LhaL queue 1hls ls C(1) So Lhe LoLal ume ls C(k). lf kn Lhls ls a blg wln! So Lhe complexlLy ls lndependenL" of Lhe number of nodes! 2014-leb-23 2014-leb-22 HEAP! : usually, Lho, lL's a heap s are usually lmplemenLed uslng a &'#( daLa sLrucLure, whlch ls a klnd of blooty ttee (nexL lecLure Loplc) [n8: "lreesLore" / "Lhe heap" ls nC1 a heap ln Lhls sense! Confuslng!]
1he "heap properLy" (Lrue for each node ln Lree, see CS240): value of parenL node >= value of (boLh) chlldren 1hls means LhaL Lhe largesL elemenL ls always aL Lhe rooL lnserL/deleLe means sLarL aL rooL and swap posluons wlLh chlldren 1here are many varleues of heap lmplemenLauons wlLh dlerenL performance characLerlsucs, buL ln Lhe vanllla verslon enter, leave, and lookup are all C(log n), peek ls C(1) Lho 2014-leb-22 Whlch ls besL? Assume n elemenLs, k dlsuncL prlorlues, slmllar freq of enterPQ/ leavePQ lf log 2 n >> k, Lhen LCL ls beuer lf log 2 n k, Lhen heap ls beuer lf we have 1,000,000 elemenLs, Lhen log 2 n == 20 So unless Lhere are very few prlorlues, (l.e., k ls very small), Lhe heap ls llkely Lo be Lhe besL cholce
2014-leb-22 lO lmplemeotouoo enterPQ leavePQ firstPQ LlsL-of-llsLs (LCL) C(k) C(1) C(1) eap C(log 2 n) C(log 2 n) C(1) 4. 1he C++ memory model, llnked sLrucLures, and a few more Au1s CS138 - WlnLer 2014 rof. Mlke Codfrey