最重要的一条规则 编写代码时最重要的一条规则是:检查周围的代码并尝试模仿它。 作为维护人员,如果收到的补丁明显与周围代码的编码风格不同,这是令人沮丧的。这是不尊重人的,就像某人穿着泥泞的鞋子走进一间一尘不染的房子。 因此,无论本文推荐的是什么,如果已经编写了代码并且您正在对其进行修补,请保持其当前的样式一致,即使它不是您最喜欢的样式。一般规则 这里列出了最明显和最重要的一般规则。在你继续阅读其他章节之前,请仔细检查它们。使用C99标准不使用制表符,而是使用空格每个缩进级别使用4个空格在关键字和左括号之间使用一个空格在函数名和左括号之间不要使用空格int32tasum(4,3);OKint32tasum(4,3);Wrong不要在变量函数宏类型中使用或前缀。这是为C语言本身保留的对于严格的模块私有函数,使用prvname前缀对于包含下划线char的变量函数宏类型,只能使用小写字母左花括号总是与关键字(for,while,do,switch,if,)在同一行for(i0;i5;i){OK}for(i0;i5;i){Wrong}for(i0;i5;i)Wrong{}在比较操作符和赋值操作符之前和之后使用单个空格微信公众号:嵌入式大杂烩int32a34;OKfor(a0;a5;a)OKa34;Wronga34;Wrongfor(a0;a5;a)Wrong每个逗号后用单空格微信公众号:嵌入式大杂烩funcname(5,4);OKfuncname(4,3);Wrong不要初始化静态和全局变量为0(或NULL),让编译器为您做staticint32OKstaticint32tb4;OKstaticint32ta0;Wrongvoidmyfunc(void){staticint32OKstaticcharabc0;Wrong}在同一行中声明所有相同类型的局部变量voidmyfunc(void){OKWrong,variablewithchartypealreadyexistschara,b;OK}按顺序声明局部变量 i。自定义结构和枚举 ii。整数类型,更宽的无符号类型优先 iii。单双浮点intmyfunc(void){1FPointerstoo2uint32int32uint16int16。。。3}总是在块的开头声明局部变量,在第一个可执行语句之前在for循环中声明计数器变量OKfor(sizeti0;i10;i)OK,for(i0;i10;i){if(。。。){}}if(i10){}Wfor(i0;i10;i)。。。避免在声明中使用函数调用来赋值变量,除了单个变量voida(void){Avoidfunctioncallswhendeclaringvariableint32ta,bsum(1,2);Usethisint32ta,b;bsum(1,2);Thisisokuint8ta3,b4;}除了char、float或double之外,始终使用stdint。h标准库中声明的类型。例如,8位的uint8t等不要使用stdbool。h库。分别使用1或0表示真或假OKuint8status0;Wrongincludestdbool。永远不要与真实相比较。例如,使用if(checkfunc()){}替换if(checkfunc()1)总是将指针与空值进行比较。。。OK,compareagainstNULLif(ptrNULLptr!NULL){}Wrongif(ptr!ptr){}总是使用前增量(和递减),而不是后增量(和递减)int32ta0;。。。a;WOKfor(sizetj0;j10;j){}OK总是使用sizet作为长度或大小变量如果函数不应该修改指针所指向的内存,则总是使用const作为指针如果不应该修改函数的形参或变量,则总是使用constWhendcouldbemodified,datapointedtobydcouldnotbemodifiedvoidmyfunc(constvoidd){}Whendanddatapointedtobydbothcouldnotbemodifiedvoidmyfunc(constvoidconstd){}Notrequired,itisadvisedvoidmyfunc(constsizetlen){}Whendshouldnotbemodifiedinsidefunction,onlydatapointedtobydcouldbemodifiedvoidmyfunc(voidconstd){}当函数可以接受任何类型的指针时,总是使用void,不要使用uint8t。函数在实现时必须注意正确的类型转换Tosenddata,functionshouldnotmodifymemorypointedtobydatavariablethusconstkeywordisimportantTosendgenericdata(ortowritethemtofile)anytypemaybepassedfordata,thususevoidOKexamplevoidsenddata(constvoiddata,sizetlen){OKDonotcastvoidorconstvoidconstuint8Functionhandlespropertypeforinternalusage}voidsenddata(constvoiddata,intlen){Wrong,notnotuseint}总是使用括号和sizeof操作符不要使用变长数组。使用动态内存分配代替标准Cmalloc和自由函数,或者如果库项目提供了自定义内存分配,使用它的实现看看LwMEM,一个自定义内存管理库。OKincludestdlib。hvoidmyfunc(sizetsize){int32arrmalloc(sizeof(arr)n);OK,Allocatememoryarrmalloc(sizeofarrn);Wrong,bracketsforsizeofoperatoraremissingif(arrNULL){FAIL,nomemory}free(arr);Freememoryafterusage}Wrongvoidmyfunc(sizetsize){int32tarr〔size〕;Wrong,donotuseVLA}总是将variable与0进行比较,除非它被视为布尔类型永远不要将布尔处理的变量与0或1进行比较。用NOT(!)代替sizetlength5;Countervariableuint8tisok0;Booleantreatedvariableif(length)Wrong,lengthisnottreatedasbooleanif(length0)OK,lengthistreatedascountervariablecontainingmultivalues,notonly0or1if(length0)OK,lengthistreatedascountervariablecontainingmultivalues,notonly0or1if(isok)OK,variableistreatedasbooleanif(!isok)OK,if(isok1)Wrong,nevercomparebooleanvariableagainst1!if(isok0)Wrong,use!fornegativecheck对于注释,总是使用comment,即使是单行注释在头文件中总是包含带有extern关键字的c检查每个函数都必须包含doxygenenabled注释,即使函数是静态的使用英文名称文本的函数,变量,注释变量使用小写字母如果变量包含多个名称,请使用下划线。forceredraw。不要使用forceRedraw对于C标准库的包含文件,请始终使用和。例如,includestdlib。h对于自定义库,请始终使用。例如,includemylibrary。h当转换为指针类型时,总是将星号与类型对齐,例如。uint8tt(uint8t)varwidthdifftype始终尊重项目或库中已经使用的代码风格注释不允许以开头的注释。总是使用comment,即使是单行注释Thisiscomment(wrong)Thisiscomment(ok)对于多行注释,每行使用空格星号Thisismultilinecomments,writtenin2lines(ok)Wrong,usedoubleasteriskonlyfordoxygendocumentationSinglelinecommentwithoutspacebeforeasterisk(wrong)Singlelinecommentinmultilineconfiguration(wrong)Singlelinecomment(ok)注释时使用12个缩进(124个空格)偏移量。如果语句大于12个缩进,将注释4空格对齐(下面的例子)到下一个可用缩进voidmyfunc(void){chara,b;acallfuncreturningchara(a);Thisiscommentwith124spacesindentfrombeginningoflinebcallfuncreturningcharabutfuncnameisverylong(a);Thisiscomment,alignedto4spacesindent}函数每个可以从模块外部访问的函数都必须包含函数原型(或声明)函数名必须小写,可以用下划线分隔OKvoidmyfunc(void);voidmyfunc(void);WrongvoidMYFunc(void);voidmyFunc();当函数返回指针时,将星号对齐到返回类型OKconstcharmyfunc(void);mystructtmyfunc(int32ta,int32tb);Wrongconstcharmyfunc(void);mystructtmyfunc(void);对齐所有的功能原型(使用相同相似的功能)以提高可读性OK,functionnamesalignedvoidset(int32ta);mytypetget(void);myptrtgetptr(void);Wrongvoidset(int32ta);constcharget(void);函数实现必须在单独的行中包含返回类型和可选的其他关键字OKint32tfoo(void){return0;}OKstaticconstchargetstring(void){returnHelloworld!r;}Wrongint32tfoo(void){return0;}变量使变量名全部小写,下划线字符可选OKint32int32int32Wrongint32tA;int32tmyVint32tMYV按类型将局部变量分组在一起voidfoo(void){int32ta,b;OKWrong,chartypealreadyexists}不要在第一个可执行语句之后声明变量voidfoo(void){int32abar();int32Wrong,thereisalreadyexecutablestatement}你可以在下一个缩进级别中声明新的变量int32ta,b;afoo();if(a){int32tc,d;OK,canddareinifstatementscopecfoo();int32Wrong,therewasalreadyexecutablestatementinsideblock}用星号声明指针变量与类型对齐OKW当声明多个指针变量时,可以使用星号对变量名进行声明OKcharp,n;结构、枚举类型定义结构名或枚举名必须小写,单词之间有下划线字符结构或枚举可以包含typedef关键字所有结构成员都必须小写所有枚举成员必须是大写的结构枚举必须遵循doxygen文档语法 在声明结构体时,它可以使用以下三种不同的选项之一: 1、当结构体仅用名称声明时,它的名称后不能包含t后缀。structstructname{}; 2、当只使用typedef声明结构时,它的名称后面必须包含t后缀。typedefstruct{} 3、当结构用name和typedef声明时,它不能包含t作为基本名称,它必须在它的名称后面包含t后缀作为typedef部分。typedefstructstructname{} 错误声明的例子及其建议的纠正:aandbmustbeseparatedto2linesNameofstructurewithtypedefmustincludetsuffixtypedefstruct{int32ta,b;}a;Correctedversiontypedefstruct{int32int32}Wrongname,itmustnotincludetsuffixstructnamet{int32int32};Wrongparameters,mustbealluppercasetypedefenum{MYENUMTESTA,myenumtestb,}在声明时初始化结构时,使用C99初始化风格OKata{。a4,。b5,};Wrongata{1,2};当为函数句柄引入newtypedef时,使用fn后缀Functionaccepts2parametersandreturnsuint8tNameoftypedefhasfnsuffixtypedefuint8t(myfunctypedeffn)(uint8tp1,constcharp2);复合语句每个复合语句必须包括左花括号和右花括号,即使它只包含1个嵌套语句每个复合语句必须包含单个缩进;嵌套语句时,每个嵌套包含1个缩进大小OKif(c){doa();}else{dob();}Wrongif(c)doa();elsedob();Wrongif(c)doa();elsedob();在if或ifelseif语句的情况下,else必须与第一条语句的右括号在同一行OKif(a){}elseif(b){}else{}Wrongif(a){}else{}Wrongif(a){}else{}在dowhile语句的情况下,while部分必须与do部分的右括号在同一行OKdo{int32adoa();dob(a);}while(check());Wrongdo{。。。}while(check());Wrongdo{。。。}while(check());每一个开括号都需要缩进if(a){doa();}else{dob();if(c){doc();}}不要做没有花括号的复合语句,即使是单个语句。下面的例子展示了一些不好的做法if(a)dob();elsedoc();if(a)doa();elsedob();空while循环、dowhile循环或for循环必须包含花括号OKwhile(isregisterbitset()){}Wrongwhile(isregisterbitset());while(isregisterbitset()){}while(isregisterbitset()){}如果while(或for、dowhile等)为空(嵌入式编程中也可能是这种情况),请使用空的单行括号Waitforbittobesetinembeddedhardwareunituint32taddrHWPERIPHREGISTERADDR;Waitbit13tobereadywhile(addr(113)){}OK,emptyloopcontainsnospacesinsidecurlybracketswhile(addr(113)){}Wrongwhile(addr(113)){Wrong}while(addr(113));Wrong,curlybracketsaremissing。Canleadtocompilerwarningsorunintentionalbugs尽量避免在循环块内递增变量,参见示例Notrecommendedint32ta0;while(a10){。。。。。。a;}Betterfor(sizeta0;a10;a){}Better,ifincmaynothappenineverycyclefor(sizeta0;a10;){if(。。。){a;}}分支语句为每个case语句添加单个缩进使用额外的单缩进break语句在每个case或defaultOK,everycasehassingleindentOK,everybreakhasadditionalindentswitch(check()){case0:doa();case1:dob();default:}Wrong,caseindentmissingswitch(check()){case0:doa();case1:dob();default:}Wrongswitch(check()){case0:doa();Wrong,breakmusthaveindentasitisundercasecase1:dob();Wrong,default:}总是包含default语句OKswitch(var){case0:dojob();default:}Wrong,defaultismissingswitch(var){case0:dojob();}如果需要局部变量,则使用花括号并在里面放入break语句。将左花括号放在case语句的同一行switch(a){OKcase0:{int32ta,b;a5;。。。}Wrongcase1:{int32}Wrong,breakshallbeinsidecase2:{int32}}宏和预处理指令总是使用宏而不是文字常量,特别是对于数字所有的宏必须是全大写的,并带有下划线字符(可选),除非它们被明确标记为function,将来可能会被常规函数语法替换OKdefineMYMACRO(x)((x)(x))Wrongdefinesquare(x)((x)(x))总是用圆括号保护输入参数OKdefineMIN(x,y)((x)(y)?(x):(y))WrongdefineMIN(x,y)xy?x:y总是用括号保护最终的宏计算WrongdefineMIN(x,y)(x)(y)?(x):(y)defineSUM(x,y)(x)(y)ImagineresultofthisequationusingwrongSUMimplementationint32tx5SUM(3,4);Expectedresultis5735int32tx5(3)(4);Itisevaluatedtothis,finalresult19whichisnotwhatweexpectCorrectimplementationdefineMIN(x,y)((x)(y)?(x):(y))defineSUM(x,y)((x)(y))当宏使用多个语句时,使用dowhile(0)语句保护它typedefstruct{int32tpx,}DefinenewpointWrongimplementationDefinemacrotosetpointdefineSETPOINT(p,x,y)(p)px(x);(p)py(y)2statements。LastoneshouldnotimplementsemicolonSETPOINT(p,3,4);Setpointtoposition3,4。Thisevaluatesto。。。(p)px(3);(p)py(4);。。。tothis。Inthisexamplethisisnotaproblem。Considerthisuglycode,howeveritisvalidbyCstandard(notrecommended)if(a)Ifaistrueif(b)IfbistrueSETPOINT(p,3,4);Setpointtox3,y4elseSETPOINT(p,5,6);Setpointtox5,y6Evaluatestocodebelow。Doyouseetheproblem?if(a)if(b)(p)px(3);(p)py(4);else(p)px(5);(p)py(6);Orifwerewriteitalittleif(a)if(b)(p)px(3);(p)py(4);else(p)px(5);(p)py(6);Askyourselfaquestion:Towhichifstatementelsekeywordbelongs?Basedonfirstpartofcode,answerisstraightforward。ToinnerifstatementwhenwecheckbconditionActualanswer:CompilationerroraselsebelongsnowhereBetterandcorrectimplementationofmacrodefineSETPOINT(p,x,y)do{(p)px(x);(p)py(y);}while(0)2statements。NosemicolonafterwhileloopOrevenbetterdefineSETPOINT(p,x,y)do{Backslashindicatesstatementcontinuesinnewline(p)px(x);(p)py(y);}while(0)2statements。NosemicolonafterwhileloopNoworiginalcodeevaluatestoif(a)if(b)do{(p)px(3);(p)py(4);}while(0);elsedo{(p)px(5);(p)py(6);}while(0);Everypartofiforelsecontainsonly1innerstatement(dowhile),hencethisisvalidevaluationTomakecodeperfect,usebracketsforeveryififelseelsestatementsif(a){Ifaistrueif(b){IfbistrueSETPOINT(p,3,4);Setpointtox3,y4}else{SETPOINT(p,5,6);Setpointtox5,y6}}不缩进子语句内if语句OKifdefined(XYZ)ifdefined(ABC)dowhenABCdefinedendifdefined(ABC)elsedefined(XYZ)DowhenXYZnotdefinedendif!defined(XYZ)Wrongifdefined(XYZ)ifdefined(ABC)dowhenABCdefinedendifdefined(ABC)elsedefined(XYZ)DowhenXYZnotdefinedendif!defined(XYZ)文档 文档化的代码允许doxygen解析和通用的htmlpdflatex输出,因此正确地执行是非常重要的。对变量、函数和结构枚举使用doxygen支持的文档样式经常使用作为doxygen,不要使用始终使用5x4空格(5个制表符)作为文本行开始的偏移量briefHoldspointertofirstentryinlinkedlistBeginningofthistextis5tabs(20spaces)每个结构枚举成员都必须包含文档注释的开头使用12x4空格偏移量briefThisispointstructoteThisstructureisusedtocalculateallpointrelatedstufftypedefstruct{int32!PointXcoordinateint32!PointYcoordinateint32!Pointsize。Sincecommentisverybig,youmaygotonextline}briefPointcolorenumerationtypedefenum{COLORRED,!Redcolor。Thiscommenthas12x4spacesoffsetfrombeginningoflineCOLORGREEN,!GreencolorCOLORBLUE,!Bluecolor}函数的文档必须在函数实现中编写(通常是源文件)函数必须包括简要和所有参数文档如果每个参数分别为in或out输入和输出,则必须注意如果函数返回某个值,则必须包含返回形参。这不适用于void函数函数可以包含其他doxygen关键字,如note或warning在参数名和描述之间使用冒号:briefSum2numbersparam〔in〕a:Firstnumberparam〔in〕b:SecondnumberreturnSumofinputvaluesint32tsum(int32ta,int32tb){}briefSum2numbersandwriteittopointeroteThisfunctiondoesnotreturnvalue,itstoresittopointerinsteadparam〔in〕a:Firstnumberparam〔in〕b:Secondnumberparam〔out〕result:Outputvariableusedtosaveresultvoidvoidsum(int32ta,int32tb,int32tresult){}如果函数返回枚举的成员,则使用ref关键字指定哪个成员briefMyenumerationtypedefenum{MYERR,!ErrorvalueMYOK!OKvalue}briefChecksomevaluereturnrefMYOKonsuccess,memberofrefmyenumtotherwisemyenumtcheckvalue(void){returnMYOK;}对常量或数字使用符号(NULLNULL)briefGetdatafrominputarrayparam〔in〕in:InputdatareturnPointertooutputdataonsuccess,NULLotherwiseconstvoidgetdata(constvoidin){}宏的文档必须包括hideinitializerdoxygen命令briefGetminimalvaluebetweenxandyparam〔in〕x:Firstvalueparam〔in〕y:SecondvaluereturnMinimalvaluebetweenxandyhideinitializerdefineMIN(x,y)((x)(y)?(x):(y))头源文件在文件末尾留下一个空行每个文件都必须包括文件的doxygen注释和后跟空行的简要描述(使用doxygen时)filetemplate。hbriefTemplateincludefileHereisemptyline每个文件(头文件或源文件)必须包含许可证(开始注释包括单个星号,因为doxygen必须忽略这个)使用与项目库已经使用的相同的许可证filetemplate。hbriefTemplateincludefileCopyright(c)yearFirstNameLASTNAMEPermissionisherebygranted,freeofcharge,toanypersonobtainingacopyofthissoftwareandassociateddocumentationfiles(theSoftware),todealintheSoftwarewithoutrestriction,includingwithoutlimitationtherightstouse,copy,modify,merge,publish,distribute,sublicense,andorsellcopiesoftheSoftware,andtopermitpersonstowhomtheSoftwareisfurnishedtodoso,subjecttothefollowingconditions:TheabovecopyrightnoticeandthispermissionnoticeshallbeincludedinallcopiesorsubstantialportionsoftheSoftware。THESOFTWAREISPROVIDEDASIS,WITHOUTWARRANTYOFANYKIND,EXPRESSORIMPLIED,INCLUDINGBUTNOTLIMITEDTOTHEWARRANTIESOFMERCHANTABILITY,FITNESSFORAPARTICULARPURPOSEANDNONINFRINGEMENT。INNOEVENTSHALLTHEAUTHORSORCOPYRIGHTHOLDERSBELIABLEFORANYCLAIM,DAMAGESOROTHERLIABILITY,WHETHERINANACTIONOFCONTRACT,TORTOROTHERWISE,ARISINGFROM,OUTOFORINCONNECTIONWITHTHESOFTWAREORTHEUSEOROTHERDEALINGSINTHESOFTWARE。Thisfileispartoflibraryname。Author:FirstNameLASTNAMEoptionalemailexample。com头文件必须包含保护符ifndef头文件必须包含c检查在c检查之外包含外部头文件首先用STLC文件包含外部头文件,然后是应用程序自定义文件头文件必须包含其他所有头文件,以便正确编译,但不能包含更多头文件(如果需要,。c应该包含其余的头文件)头文件必须只公开模块公共变量类型函数在头文件中使用extern作为全局模块变量,稍后在源文件中定义它们file。h。。。ifndef。。。externint32Thisisglobalvariabledeclarationinheaderendiffile。c。。。int32Actuallydefinedinsource不要把。c文件包含在另一个。c文件中。c文件应该首先包含相应的。h文件,然后是其他文件,除非另有明确的必要在头文件中不包含模块私有声明头文件示例(示例中没有license)LicensecomeshereifndefTEMPLATEHDRHdefineTEMPLATEHDRHIncludeheadersifdefcplusplusexternC{endifcplusplusFilecontenthereifdefcplusplus}endifcplusplusendifTEMPLATEHDRH猜你喜欢: 分享一种简单、实用的测量程序运行时间的方法 简单认识认识mqtt及mosquitto STM32的ISP是怎么一回事? 手把手教你在STM32上使用nanopb 1024G嵌入式资源大放送!包括但不限于CC、单片机、Linux等。私信回复1024,即可免费获取!