Jump Tables via Function Pointer Arrays in C/C++

文章推薦指數: 80 %
投票人數:10人

Here's a look at the use of arrays of function pointers in C/C++ as jump ... the function that uses it–and thus keeping it off the stack. Home Home Bloggers MichaelBarr NigelJones MiroSamek PostsbyCategory NewsletterSignup AboutUs ContactUs Login    Searchfor: «InPraiseoftheCPreprocessor’s#errorDirective RS-485TransmitEnableSignalControl» JumpTablesviaFunctionPointerArraysinC/C++ Thursday,December17th,2009byNigelJones AlsoavailableinPDFversion. Jumptables,alsocalledbranchtables,areanefficientmeansofhandlingsimilareventsinsoftware.Here’salookattheuseofarraysoffunctionpointersinC/C++asjumptables. Examinationofassemblylanguagecodethathasbeencraftedbyanexpertwillusuallyrevealextensiveuseoffunction“branchtables.”Branchtables(a.k.a.,jumptables)areusedbecausetheyofferauniqueblendofcompactnessandexecutionspeed,particularlyonmicroprocessorsthatsupportindexedaddressing.WhenoneexaminestypicalC/C++code,however,thebranchtable(i.e.,anarrayoffuntionpointers)isamuchrarerbeast.ThepurposeofthisarticleistoexaminewhybranchtablesarenotusedbyC/C++programmersandtomakethecasefortheirextensiveuse.Realworldexamplesoftheiruseareincluded. Functionpointers IntalkingtoC/C++programmersaboutthistopic,threereasonsareusuallycitedfornotusingfunctionpointers.Theyare: Theyaredangerous Agoodoptimizingcompilerwillgenerateajumptablefromaswitchstatement,soletthecompilerdothework Theyaretoodifficulttocodeandmaintain Arefunctionpointersdangerous? Thisschoolofthoughtcomesabout,becausecodethatindexesintoatableandthencallsafunctionbasedontheindexhasthecapabilitytoendupjustaboutanywhere.Forinstance,considerthefollowingcodefragment: void(*pf[])(void)={fna,fnb,fnc,…,fnz}; voidtest(constINTjump_index) { /*Callthefunctionspecifiedbyjump_index*/ pf[jump_index](); } Theabovecodedeclarespf[]tobeanarrayofpointerstofunctions,eachofwhichtakesnoargumentsandreturnsvoid.Thetest()functionsimplycallsthespecifiedfunctionviathearray.Asitstands,thiscodeisdangerousforthefollowingreasons. pf[]isaccessiblebyanyone Intest(),thereisnoboundschecking,suchthatanerroneousjump_indexwouldspelldisaster Amuchbetterwaytocodethisthatavoidstheseproblemsisasfollows voidtest(uint8_tconstump_index) { staticvoid(*pf[])(void)={fna,fnb,fnc,…,fnz}; if(jump_indexinterval!=0;ptr++) { if(!(time%ptr->interval)) { /*Timetocallthefunction*/ (ptr->proc)(); } } } } } Inthiscase,wedefineourowndatatype(TIMED_TASK)thatconsistssimplyofanintervalandapointertoafunction.WethendefineanarrayofTIMED_TASK,andinitializeitwiththelistoffunctionsthataretobecalledandtheircallinginterval.Inmain(),wehavethestartupcodewhichmustenableaperiodictimerinterruptthatincrementsthevolatilevariabletickatafixedinterval.Wethenentertheinfiniteloop. Theinfiniteloopchecksforanon-zerotickvalue,decrementsthetickvariableandcomputestheelapsedtimesincetheprogramstartedrunning.Thecodethensimplystepsthrougheachofthetasks,toseewhetheritistimeforthatonetobeexecutedand,ifso,callsitviathefunctionpointer. Ifyourapplicationonlyconsistsoftwoorthreetasks,thenthisapproachisprobablyoverkill.However,ifyourprojecthasalargenumberoftimedtasks,oritislikelythatyouwillhavetoaddtasksinthefuture,thenthisapproachisratherpalatable.Notethataddingtasksand/orchangingintervalssimplyrequireseditingofthetimed_task[]array.Nocode,perse,hastobechanged. Interruptvectortables Thefourthapplicationoffunctionjumptablesisthearrayofinterruptvectors.Onmostprocessors,theinterruptvectorsareincontiguouslocations,witheachvectorrepresentingapointertoaninterruptserviceroutinefunction.Dependinguponthecompiler,theworkmaybedoneforyouimplicitly,oryoumaybeforcedtogeneratethefunctiontable.Inthelattercase,implementingthevectorsviaaswitchstatementwillnotwork! Hereisthevectortablefromtheindustrialpowersupplyprojectmentionedabove.ThisprojectwasimplementedusingaWhitesmiths’compileranda68HC11microncontroller. IMPORTVOID_stext();/*68HC11-specificstartuproutine*/ staticVOID(*const_vectab[])()= { SCI_Interrupt, /*SCI*/ badSPI_Interrupt, /*SPI*/ badPAI_Interrupt, /*Pulseaccinput*/ badPAO_Interrupt, /*Pulseaccoverf*/ badTO_Interrupt, /*Timeroverf*/ badOC5_Interrupt, /*Outputcompare5*/ badOC4_Interrupt, /*Outputcompare4*/ badOC3_Interrupt, /*Outputcompare3*/ badOC2_Interrupt, /*Outputcompare2*/ badOC1_Interrupt, /*Outputcompare1*/ badIC3_Interrupt, /*Inputcapture3*/ badIC2_Interrupt, /*Inputcapture2*/ badIC1_Interrupt, /*Inputcapture1*/ RTI_Interrupt, /*Realtime*/ Uart_Interrupt, /*IRQ*/ PFI_Interrupt, /*XIRQ*/ badSWI_Interrupt, /*SWI*/ IlOpC_Interrupt, /*illegal*/ _stext, /*copfail*/ _stext, /*copclockfail*/ _stext, /*RESET*/ }; Acoupleofpointsareworthmaking: Theaboveisinsufficienttolocatethetablecorrectlyinmemory.Thishastobedonevialinkerdirectives. Notethatunusedinterruptsstillhaveanentryinthetable.Doingsoensuresthatthetableiscorrectlyalignedandtrapscanbeplacedonunexpectedinterrupts. Ifanyoftheseexampleshaswhetyourappetiteforusingarraysoffunctionpointers,butyouarestilluncomfortablewiththedeclarationcomplexity,thenfearnot!Youwillfindavarietyofdeclarations,rangingfromthestraightforwardtothedownrightappallingbelow.Theexamplesareallreasonablypracticalinthesensethatthedesiredfunctionalityisnotoutlandish(thatis,therearenodeclarationsforarraysofpointerstofunctionsthattakepointerstoarraysoffunctionpointersandsoon). Declarationandusehints AlloftheexamplesbelowadheretoconventionsthatIhavefoundtobeusefulovertheyears,specifically: 1.Alloftheexamplesareprecededbystatic.Thisisdoneontheassumptionthatthescopeofafunctiontableshouldbehighlylocalized,ideallywithinanenclosingfunction. 2.Ineveryexamplethearraypf[]isalsoprecededwithconst.Thisdeclaresthatthepointersinthearraycannotbemodifiedafterinitialization.Thisisthenormal(andsafe)usagescenario. 3.Therearetwosyntacticallydifferentwaysofinvokingafunctionviaapointer.Ifwehaveafunctionpointerwiththedeclaration: void(*fnptr)(int); /*fnptrisafunctionpointer*/ Thenitmaybeinvokedusingeitherofthesemethods: fnptr(3); /*Method1ofinvokingthefunction*/ (*fnptr)(3); /*Method2ofinvokingthefunction*/ Theadvantageofthefirstmethodisanunclutteredsyntax.However,itmakesitlookasiffnptrisafunction,asopposedtobeingafunctionpointer.Someonemaintainingthecodemayendupsearchinginvainforthefunctionfnptr().Withmethod2,itismuchclearerthatwearedereferencingapointer.However,whenthedeclarationsgetcomplex,theadded(*)canbeasignificantburden.Throughouttheexamples,eachsyntaxisshown.Inpractice,thelattersyntaxseemstobemorepopular–andyoushoulduseonlyone. 4.Ineveryexample,thesyntaxforusingatypedefisalsogiven.Itisquitepermissibletouseatypedeftodefineacomplexdeclaration,andthenusethenewtypelikeasimpletype.Ifwestaywiththeexampleabove,thenanalternativedeclarationis: typedefvoid(*PFV_I)(int); /*DeclareaPVFV_Itypedvariableandinitit*/ PFV_Ifnptr=fna; /*Callfnawithparameter3usingmethod1*/ fnptr(3); /*Callfnawithparameter3usingmethod2*/ (*fnptr)(3); ThetypedefdeclaresthetypePFV_Itobeapointertoafunctionthatreturnsvoidandispassedaninteger.Wethensimplydeclarefnptrtoavariableofthistype,anduseit.Typedefsareverygoodwhenyouregularlyuseacertainfunctionpointertype,sinceitsavesyouhavingtorememberandtypeinthedeclaration.Thedownsideofusingatypedef,isthefactthatitisnotobviousthatthevariablethathasbeendeclaredisapointertoafunction.Thus,justasforthetwoinvocationmethodsabove,youcangainsyntacticalsimplicitybyhidingtheunderlyingfunctionality. Inthetypedefs,aconsistentnamingconventionisused.EverytypestartswithPF(PointertoFunction)andisthenfollowedwiththereturntype,followedbyanunderscore,thefirstparametertype,underscore,secondparametertypeandsoon.Forvoid,boolean,char,int,long,floatanddouble,thecharactersV,B,C,I,L,S,Dareused.(NotetheuseofS(ingle)forfloat,toavoidconfusionwithF(unction)).Forapointertoadatatype,thetypeisprecededwithP.ThusPLisapointertoalong.Ifaparameterisconst,thenacappearsintheappropriateplace.Thus,cPLisaconstpointertoalong,whereasaPcLisapointertoaconstlong,andcPcLisaconstpointertoaconstlong.Forvolatilequalifiers,visused.Forunsignedtypes,auprecedesthebasetype.Foruserdefineddatatypes,youareonyourown! Anextremeexample:PFcPcI_uI_PvuC.ThisisapointertoafunctionthatreturnsaconstpointertoaconstIntegerthatispassedanunsignedintegerandapointertoavolatileunsignedchar. Functionpointertemplates Thefirstelevenexamplesaregenericinthesensethattheydonotusememoryspacequalifiersandhencemaybeusedonanytarget.Example12showshowtoaddmemoryspacequalifiers,suchthatallthecomponentsofthedeclarationendupinthecorrectmemoryspaces. Example1 pf[]isastaticarrayofpointerstofunctionsthattakeanINTasanargumentandreturnvoid. voidfna(INT); //Exampleprototypeofafunctiontobecalled //Declarationusingtypedef typedefvoid(*constPFV_I)(INT); staticPFV_Ipf[]={fna,fnb,fnc,…fnz); //Directdeclaration staticvoid(*constpf[])(INT)={fna,fnb,fnc,…fnz}; //Exampleuse INTa=6; pf[jump_index](a); //Callingmethod1 (*pf[jump_index])(a); //Callingmethod2 Example2 pf[]isastaticarrayofpointerstofunctionsthattakeapointertoanINTasanargumentandreturnvoid. voidfna(INT*); //Exampleprototypeofafunctiontobecalled //Declarationusingtypedef typedefvoid(*constPFV_PI)(INT*); staticPVF_PI[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticvoid(*constpf[])(INT*)={fna,fnb,fnc,…fnz}; //Exampleuse INTa=6; pf[jump_index](&a); //Callingmethod1 (*pf[jump_index])(&a); //Callingmethod2 Example3 pf[]isastaticarrayofpointerstofunctionsthattakeanINTasanargumentandreturnaCHAR CHARfna(INT); //Exampleprototypeofafunctiontobecalled //Declarationusingtypedef typedefCHAR(*constPFC_I)(INT); staticPVC_I[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticCHAR(*constpf[])(INT)={fna,fnb,fnc,…fnz}; //Exampleuse INTa=6; CHARres; res=pf[jump_index](a); //Callingmethod1 res=(*pf[jump_index])(a); //Callingmethod2 Example4 pf[]isastaticarrayofpointerstofunctionsthattakeanINTasanargumentandreturnapointertoaCHAR. CHAR*fna(INT); //Exampleprototypeofafunctiontobecalled //Declarationusingtypedef typedefCHAR*(*constPFPC_I)(INT); staticPVPC_I[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticCHAR*(*constpf[])(INT)={fna,fnb,fnc,…fnz}; //Exampleuse INTa=6; CHAR*res; res=pf[jump_index](a); //Callingmethod1 res=(*pf[jump_index])(a); //Callingmethod2 Example5 pf[]isastaticarrayofpointerstofunctionsthattakeanINTasanargumentandreturnapointertoaconstCHAR(i.e.thepointermaybemodified,butwhatitpointstomaynot). constCHAR*fna(INT); //Exampleprototypeofafunctiontobecalled //Declarationusingtypedef typedefconstCHAR*(*constPFPcC_I)(INT); staticPVPcC_I[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticconstCHAR*(*constpf[])(INT)={fna,fnb,fnc,…fnz}; //Exampleuse INTa=6; constCHAR*res; res=pf[jump_index](a); //Callingmethod2 res=(*pf[jump_index])(a); //Callingmethod2 Example6 pf[]isastaticarrayofpointerstofunctionsthattakeanINTasanargumentandreturnaconstpointertoaCHAR(i.e.thepointermaynotbemodified,butwhatitpointstomaybemodified). CHAR*constfna(INTi);//Exampleprototypeofafunctiontobecalled //Declarationusingtypedef typedefCHAR*const(*constPFcPC_I)(INT); staticPVcPC_I[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticCHAR*const(*constpf[])(INT)={fna,fnb,fnc,…fnz}; //Exampleuse INTa=6; CHAR*constres=pf[jump_index](a); //Callingmethod1 CHAR*constres=(*pf[jump_index])(a); //Callingmethod2 Example7 pf[]isastaticarrayofpointerstofunctionsthattakeanINTasanargumentandreturnaconstpointertoaconstCHAR(i.e.thepointer,norwhatitpointstomaybemodified) constCHAR*constfna(INTi);//Examplefunctionprototype //Declarationusingtypedef typedefconstCHAR*const(*constPFcPcC_I)(INT); staticPVcPcC_I[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticconstCHAR*const(*constpf[])(INT)={fna,fnb,fnc,…fnz}; //Exampleuse INTa=6; constCHAR*constres=pf[jump_index](a); //Callingmethod1 constCHAR*constres=(*pf[jump_index])(a); //Callingmethod2 Example8 pf[]isastaticarrayofpointerstofunctionsthattakeapointertoaconstINTasanargument(i.e.thepointermaybemodified,butwhatitpointstomaynot)andreturnaconstpointertoaconstCHAR(i.e.thepointer,norwhatitpointstomaybemodified) constCHAR*constfna(constINT*i); //Exampleprototype //Declarationusingtypedef typedefconstCHAR*const(*constPFcPcC_PcI)(constINT*); staticPVcPcC_PcI[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticconstCHAR*const(*constpf[])(constINT*)={fna,fnb,fnc,…fnz}; //Exampleuse constINTa=6; constINT*aptr; aptr=&a; constCHAR*constres=pf[jump_index](aptr); //Callingmethod1 constCHAR*constres=(*pf[jump_index])(aptr);//Callingmethod2 Example9 pf[]isastaticarrayofpointerstofunctionsthattakeaconstpointertoanINTasanargument(i.e.thepointermaynotbemodified,butwhatitpointstomay)andreturnaconstpointertoaconstCHAR(i.e.thepointer,norwhatitpointstomaybemodified) constCHAR*constfna(INT*consti); //Exampleprototype //Declarationusingtypedef typedefconstCHAR*const(*constPFcPcC_cPI)(INT*const); staticPVcPcC_cPI[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticconstCHAR*const(*constpf[])(INT*const)={fna,fnb,fnc,…fnz}; //Exampleuse INTa=6; INT*constaptr=&a; constCHAR*constres=pf[jump_index](aptr); //Method1 constCHAR*constres=(*pf[jump_index])(aptr); //Method2 Example10 pf[]isastaticarrayofpointerstofunctionsthattakeaconstpointertoaconstINTasanargument(i.e.thepointernorwhatitpointstomaybemodified)andreturnaconstpointertoaconstCHAR(i.e.thepointer,norwhatitpointstomaybemodified) constCHAR*constfna(constINT*consti); //Exampleprototype //Declarationusingtypedef typedefconstCHAR*const(*constPFcPcC_cPcI)(constINT*const); staticPVcPcC_cPcI[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticconstCHAR*const(*constpf[])(constINT*const)={fna,fnb,fnc,…fnz}; //Exampleuse constINTa=6; constINT*constaptr=&a; constCHAR*constres=pf[jump_index](aptr); //Method1 constCHAR*constres=(*pf[jump_index])(aptr); //Method2 Thisexamplemanagestocombinefiveincidencesofconstandoneofstaticintoasingledeclaration.Forallofitscomplexity,however,thisisnotanartificialexample.Youcouldgoaheadandremovealltheconstandstaticdeclarationsandthecodewouldstillwork.Itwould,however,bealotlesssafe,andpotentiallylessefficient. Justtobreakupthemonotony,hereisthesamedeclaration,butwithatwist. Example11 pf[]isastaticarrayofpointerstofunctionsthattakeaconstpointertoaconstINTasanargument(i.e.thepointernorwhatitpointstomaybemodified)andreturnaconstpointertoavolatileCHAR(i.e.thepointermaynotbemodified,butwhatitpointstomaychangeunexpectedly) volatileCHAR*constfna(constINT*consti); //Exampleprototype //Declarationusingtypedef typedefvolatileCHAR*const(*constPFcPvC_cPcI)(constINT*const); staticPVcPvC_cPcI[]={fna,fnb,fnc,…fnz}; //Directdeclaration staticvolatileCHAR*const(*constpf[])(constINT*const)={fna,fnb,fnc,…fnz}; //Exampleuse constINTa=6; constINT*constaptr=&a; volatileCHAR*constres=pf[jump_index](aptr); //Method1 volatileCHAR*constres=(*pf[jump_index])(aptr); //Method2 while(*res) ; //Waitforvolatileregistertoclear Withmemoryspacequalifiers,thingscangetevenmorehairy.Formostvendors,thememoryspacequalifieristreatedsyntacticallyasatypequalifier(suchasconstorvolatile)andthusfollowsthesameplacementrules.Forconsistency,Iplacetypequalifierstotheleftofthe“thing”beingqualified.Wheretherearemultipletypequalifiers,alphabeticorderingisused.Sincememoryspacequalifiersaretypicallycompilerextensions,theyarenormallyprecededbyanunderscore,andhencecomefirstalphabetically.Thus,anastydeclarationmaylooklikethis: _ramconstvolatileUCHARstatus_register; Todemonstratememoryspacequalifieruse,hereisexample11again,exceptthistimememoryspacequalifiershavebeenadded.Thequalifiersarenamed_m1…_m5. Example12 pf[]isastaticarrayofpointerstofunctionsthattakeaconstpointertoaconstINTasanargument(i.e.thepointernorwhatitpointstomaybemodified)andreturnaconstpointertoavolatileCHAR(i.e.thepointermaybemodified,butwhatitpointstomaychangeunexpectedly).Eachelementofthedeclarationliesinadifferentmemoryspace.Inthisparticularcase,itisassumedthatyoucanevendeclarethememoryspaceinwhichparameterspassedbyvalueappear.Thisisextreme,butisjustifiedonpedagogicalgrounds. /*Anexampleprototype.Thisdeclarationreadsasfollows. *Functionfnaispassedaconstpointerin_m5spacethatpointstoa *constintegerin_m4space.Itreturnsaconstpointerin_m2spaceto *avolatilecharacterin_m1space. */ _m1volatileCHAR*_m2constfna(_m4constINT*_m5consti); /*Declarationusingtypedef.Thisdeclarationreadsasfollows. *PFcPvC_cPcIisapointertofunctiondatatype,variablesbased *uponwhichliein_m3space.EachFunctionispassedaconst *pointerin_m5spacethatpointstoaconstintegerin_m4space. *Itreturnsaconstpointerin_m2spacetoavolatilecharacter *in_m1space. */ typedef_m1volatileCHAR*_m2const(*_m3constPFcPvC_cPcI)(_m4constINT*_m5const); staticPVcPvC_cPcI[]={fna,fnb,fnc,…fnz}; /*Directdeclaration.Thisdeclarationreadsasfollows.pf[]is *astaticallyallocatedconstantarrayin_m3spaceofpointerstofunctions. *EachFunctionispassedaconstpointerin_m5spacethatpointsto *aconstintegerin_m4space.Itreturnsaconstpointerin_m2space *toavolatilecharacterin_m1space. */ static_m1volatileCHAR*_m2const(*_m3constpf[])(_m4constINT*_m5const)={fna,fnb,fnc,…fnz}; //Declareaconstvariablethatliesin_m4space _m4constINTa=6; //Nowdeclareaconstpointerin_m5spacethatpointstoaconst //variablethatisin_m4space _m4constINT*_m5constaptr=&a; //Makethefunctioncall,andgetbackthepointer volatileCHAR*constres=pf[jump_index](&a); //Method1 volatileCHAR*constres=(*pf[jump_index])(&a); //Method2 while(*res) ; //Waitforvolatileregistertoclear Acknowledgments MythankstoMikeStevensnotonlyforreadingoverthismanuscriptandmakingsomeexcellentsuggestionsbutalsoforovertheyearsshowingmemorewaystousefunctionpointersthatIeverdreamedwaspossible. ThisarticlewaspublishedintheMay1999issueofEmbeddedSystemsProgramming.Ifyouwishtocitethearticleinyourownwork,youmayfindthefollowingMLA-styleinformationhelpful: Jones,Nigel.“ArraysofPointerstoFunctions”EmbeddedSystemsProgramming,May1999. Thisentrywasposted onThursday,December17th,2009at4:43pm andisfiledunderPublications. YoucanfollowanyresponsestothisentrythroughtheRSS2.0feed. Bothcommentsandpingsarecurrentlyclosed. Commentsareclosed. StackOverflow NigelJones NigelJonesisanembeddedsystemsconsultantwithover20yearsofexperiencedesigningelectroniccircuitsandfirmware.(fullbio) Pages ContactNigel Links ArticlesonEmbeddedSystems EmbeddedCCodingStandard EmbeddedSoftwareTraininginaBox EmbeddedSystemsDesign,Consulting,andExpertWitnessServices RecentPosts EEPROMWearLeveling Hammingdistances Peakdetectionofatimeseries BoeingDreamliner‘Bug’ Freescalecustomerservice Firmalware ShiftingStyles Theengineering–marketingdivide Replacingnestedswitcheswithmulti-dimensionalarraysofpointerstofunctions Idlingalong,(orwhattodointheidletask) RecentCommentsNigelJonesonEfficientCTips#5–Make‘local’functions‘static’jimonEfficientCTips#5–Make‘local’functions‘static’YuriGribovonEfficientCTips#5–Make‘local’functions‘static’Mico/Milang,abasiccompiler–MinaPêcheuxonEfficientCTip#12–BewaryofswitchstatementsjohnonIsMISRAcomplianceworthwhile?Categories Algorithms(16) CodingStandards(23) Compilers/Tools(25) Consulting(14) EffectiveC/C++(13) EfficientC/C++(20) FirmwareBugs(12) GeneralCissues(49) Hardware(22) LowPowerDesign(7) Minimizingmemoryconsumption(3) Publications(12) RTOSMultithreading(2) Uncategorized(32) Archives July2017 (1) June2017 (1) September2015 (1) May2015 (1) March2015 (1) February2015 (1) November2014 (1) April2014 (1) March2014 (1) April2013 (1) February2013 (1) January2013 (1) December2012 (1) July2012 (2) June2012 (2) March2012 (1) February2012 (2) December2011 (2) September2011 (2) August2011 (1) June2011 (1) May2011 (1) March2011 (1) February2011 (4) January2011 (1) December2010 (2) November2010 (3) October2010 (2) September2010 (2) August2010 (8) June2010 (2) May2010 (2) April2010 (1) March2010 (3) February2010 (4) January2010 (2) December2009 (18) November2009 (3) October2009 (7) September2009 (8) August2009 (4) July2009 (4) June2009 (4) May2009 (4) April2009 (5) March2009 (5) February2009 (6) January2009 (4) December2008 (3) November2008 (3) October2008 (1) September2008 (2) August2008 (3) July2008 (1) June2008 (3) May2008 (2) April2008 (1) February2008 (1) January2008 (3) August2007 (2) July2007 (1) June2007 (2) May2007 (2) April2007 (1) March2007 (1) December2006 (3) November2006 (2) October2006 (2) September2006 (5) TagsAVR Bitfields bootstraploading CodingStandards Compilers corrosion CPUselection CTest developmentcosts EEPROM ethics flags FRAM GCC holdingcurrent I2C IAR Independentcontractor Keil LED Lint Lookuptable LUT Medianfilter memory memoryconsumption Microchip MISRA MSP430 optimization parameterpassing PC-Lint PIC printf prototypes PWM Relays Rowley signed taxcode unsigned variablenaming volatile voltagegradient Windows EmbeddedGurus-ExpertsonEmbeddedSoftware websitebyAccentInteractive



請為這篇文章評分?