单片机C语言编程基础.docx
《单片机C语言编程基础.docx》由会员分享,可在线阅读,更多相关《单片机C语言编程基础.docx(33页珍藏版)》请在冰豆网上搜索。
单片机C语言编程基础
优先级及预处理
另外ANSI标准C还定义了如下几个宏:
_LINE_表示正在编译的文件的行号
_FILE_表示正在编译的文件的名字
_DATE_表示编译时刻的日期字符串,例如:
"25Dec2007"
_TIME_表示编译时刻的时间字符串,例如:
"12:
30:
55"
_STDC_判断该文件是不是定义成标准C程序
函数atoi与atof
函数名:
atoi
功能:
把字符串转换成长整型数
用法:
intatoi(constchar*nptr);
程序例:
###############################################################################
#include
intmain(void)
{
intn;
char*str="12345.67";
n=atoi(str);
printf("string=%sinteger=%d\n",str,n);
return0;
}
atof
把字符串转换成浮点数
doubleatof(constchar*nptr);
floatf;
f=atof(str);
printf("string=%sfloat=%f\n",str,f);
链表
链表是C语言中另外一个难点。
牵扯到结点、动态分配空间等等。
用结构作为
链表的结点是非常适合的,例如:
structnode
intdata;
structnode*next;
};
其中next是指向自身所在结构类型的指针,这样就可以把一个个结点相连,构
成链表。
链表结构的一大优势就是动态分配存储,不会像数组一样必须在定义时确定大
小,造成不必要的浪费。
用malloc和free函数即可实现开辟和释放存储单元。
其中,malloc的参数多用sizeof运算符计算得到。
链表的基本操作有:
正、反向建立链表;输出链表;删除链表中结点;在链表中
插入结点等等,都是要熟练掌握的,初学者通过画图的方式能比较形象地理解建
立、插入等实现的过程。
typedefstructnode
chardata;
}NODE;/*结点*/
正向链表
NODE*create()
charch='a';
NODE*p,*h=NULL,*q=NULL;
while(ch<'z')
p=(NODE*)malloc(sizeof(NODE));/*强制类型转换为指针*/
p->data=ch;
if(h==NULL)h=p;
elseq->next=p;
ch++;
q=p;
q->next=NULL;/*链表结束*/
returnh;
逆向建立
NODE*p,*h=NULL;
while(ch<='z')
p=(NODE*)malloc(sizeof(NODE));
p->next=h;/*不断地把head往前挪*/
h=p;
用递归实现链表逆序输出:
voidoutput(NODE*h)
if(h!
=NULL)
output(h->next);
printf("%c",h->data);
插入结点
插入结点(已有升序的链表):
NODE*insert(NODE*h,intx)
NODE*new,*front,*current=h;
while(current!
=NULL&&(current->data{front=current;current=current->next;}new=(NODE*)malloc(sizeof(NODE));new->data=x;new->next=current;if(current==h)/*判断是否是要插在表头*/h=new;elsefront->next=new;returnh;}删除结点NODE*delete(NODE*h,intx){NODE*q,*p=h;while(p!=NULL&&(p->data!=x)){q=p;p=p->next;}if(p->data==x)/*找到了要删的结点*/{if(p==h)/*判断是否要删表头*/h=h->next;elseq->next=p->next;free(p);/*释放掉已删掉的结点*/}returnh;}文件包含文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。C语言提供#include命令来实现文件包含的操作,它实际是宏替换的延伸,有两种格式:格式1:#include其中,filename为要包含的文件名称,用尖括号括起来,也称为头文件,表示预处理到系统规定的路径中去获得这个文件(即C编译系统所提供的并存放在指定的子目录下的头文件)。找到文件后,用文件内容替换该语句。格式2:#include“filename”其中,filename为要包含的文件名称。双引号表示预处理应在当前目录中查找文件名为filename的文件,若没有找到,则按系统指定的路径信息,搜索其他目录。找到文件后,用文件内容替换该语句。需要强调的一点是:#include是将已存在文件的内容嵌入到当前文件中。另外关于#include的路径也有点要说明:include支持相对路径,格式如trackant(蚁迹寻踪)所写:.代表当前目录,..代表上层目录。代码书写首先我想说明我本文阐述的是纯粹从美学的角度来写出代码,而非技术、逻辑等。以下为写出漂亮代码的七种方法:1尽快结束if语句例如下面这个JavaScript语句,看起来就很恐怖:1functionfindShape(flags,point,attribute,list){2 if(!findShapePoints(flags,point,attribute)){3 if(!doFindShapePoints(flags,point,attribute)){4 if(!findInShape(flags,point,attribute)){5 if(!findFromGuide(flags,point){6 if(list.count()>0&&flags==1){7 doSomething();8 }9 }10 }11 }12 } 13 }但如果这么写就好看得多:1functionfindShape(flags,point,attribute,list){2 if(findShapePoints(flags,point,attribute)){3 return;4 }56 if(doFindShapePoints(flags,point,attribute)){7 return;8 }910 if(findInShape(flags,point,attribute)){11 return;12 }1314 if(findFromGuide(flags,point){15 return;16 }1718 if(!(list.count()>0&&flags==1)){19 return;20 }2122 doSomething();2324}你可能会很不喜欢第二种的表述方式,但反映出了迅速返回if值的思想,也可以理解为:避免不必要的else陈述。2如果只是简单的布尔运算(逻辑运算),不要使用if语句例如:1functionisStringEmpty(str){2 if(str===""){3 returntrue;4 }5 else{6 returnfalse;7 }8}可以写为:1functionisStringEmpty(str){2 return(str==="");3}3使用空白,这是免费的例如:1functiongetSomeAngle(){2 //Somecodeherethen3 radAngle1=Math.atan(slope(center,point1));4 radAngle2=Math.atan(slope(center,point2));5 firstAngle=getStartAngle(radAngle1,point1,center);6 secondAngle=getStartAngle(radAngle2,point2,center);7 radAngle1=degreesToRadians(firstAngle);8 radAngle2=degreesToRadians(secondAngle);9 baseRadius=distance(point,center);10 radius=baseRadius+(lines*y);11 p1["x"]=roundValue(radius*Math.cos(radAngle1)+center["x"]);12 p1["y"]=roundValue(radius*Math.sin(radAngle1)+center["y"]);13 pt2["x"]=roundValue(radius*Math.cos(radAngle2)+center["y"]);14 pt2["y"]=roundValue(radius*Math.sin(radAngle2)+center["y");15 //Nowsomemorecode16}很多开发者不愿意使用空白,就好像这要收费一样。我在此并非刻意地添加空白,粗鲁地打断代码的连贯性。在实际编写代码的过程中,会很容易地发现在什么地方加入空白,这不但美观而且让读者易懂,如下:1functiongetSomeAngle(){2 //Somecodeherethen3 radAngle1=Math.atan(slope(center,point1));4 radAngle2=Math.atan(slope(center,point2));56 firstAngle=getStartAngle(radAngle1,point1,center);7 secondAngle=getStartAngle(radAngle2,point2,center);89 radAngle1=degreesToRadians(firstAngle);10 radAngle2=degreesToRadians(secondAngle);1112 baseRadius=distance(point,center);13 radius=baseRadius+(lines*y);1415 p1["x"]=roundValue(radius*Math.cos(radAngle1)+center["x"]);16 p1["y"]=roundValue(radius*Math.sin(radAngle1)+center["y"]);1718 pt2["x"]=roundValue(radius*Math.cos(radAngle2)+center["y"]);19 pt2["y"]=roundValue(radius*Math.sin(radAngle2)+center["y");20 //Nowsomemorecode21}4不要使用无谓的注释无谓的注释让人费神,这实在很讨厌。不要标出很明显的注释。在以下的例子中,每个人都知道代码表达的是“studentsid”,因而没必要标出。1functionexistsStudent(id,list){2 for(i=0;i3 student=list[i];45 //Getthestudent'sid6 thisId=student.getId();78 if(thisId===id){9 returntrue;10 }11 }12 returnfalse; 13}5不要在源文件中留下已经删除的代码,哪怕你标注了如果你使用了版本控制,那么你就可以轻松地找回前一个版本的代码。如果别人大费周折地读了你的代码,却发现是要删除的代码,这实在太恨人了。//functionthisReallyHandyFunction(){//someMagic();//someMoreMagic();//magicNumber=evenMoreMagic();// returnmagicNumber;//}6不要有太长的代码看太长的代码实在太费劲,尤其是代码本身的功能又很小。如下:1publicstaticEnumMapgetGroupCategoryDistribution(EnumMapsizes,intgroups){2 EnumMapcategoryGroupCounts=newEnumMap(Category.class);34 for(Categorycat:Category.values()){5 categoryGroupCounts.put(cat,getCategoryDistribution(sizes.get(cat),groups));6 }我并不是说非要坚持70个字符以内,但是一个比较理想的长度是控制在120个字符内。如果你把代码发布在互联网上,用户读起来就很困难。7不要在一个功能(或者函数内)有太多代码行我的一个老同事曾经说VisualC++很臭,因为它不允许你在一个函数内拥有超过10,000行代码。我记不清代码行数的上限,不知道他说的是否正确,但我很不赞成他的观点。如果一个函数超过了50行,看起来有多费劲你知道么,还有没完没了的if循环,而且你还的滚动鼠标前后对照这段代码。对我而言,超过35行的代码理解起来就很困难了。我的建议是超过这个数字就把一个函数代码分割成两个。#pragma预处理在所有的预处理指令中,#pragma指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。其格式一般为:#pragmapara其中para为参数,下面来看一些常用的参数。.1,#pragmamessagemessage参数:Message参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:#pragmamessage(“消息文本”)当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法#ifdef_X86#Pragmamessage(“_X86macroactivated!”)#endif当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_X86macroactivated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了.2,#pragmacode_seg另一个使用得比较多的pragma参数是code_seg。格式如:#pragmacode_seg(["section-name"[,"section-class"]])它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。.3,#pragmaonce#pragmaonce(比较常用)只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VisualC++6.0中就已经有了,但是考虑到兼容性并没有太多的使用它。.4,#pragmahdrstop#pragmahdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragmastartup指定编译优先级,如果使用了#pragmapackage(smart_init),BCB就会根据优先级的大小先后编译。.5,#pragmaresource#pragmaresource"*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。.6,#pragmawarning#pragmawarning(disable:450734;once:4385;error:164)等价于:#pragmawarning(disable:450734)//不显示4507和34号警告信息#pragmawarning(once:4385)//4385号警告信息仅报告一次#pragmawarning(error:164)//把164号警告信息作为一个错误。同时这个pragmawarning也支持如下格式:#pragmawarning(push[,n])#pragmawarning(pop)这里n代表一个警告等级(1---4)。#pragmawarning(push)保存所有警告信息的现有的警告状态。#pragmawarning(push,n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。#pragmawarning(pop)向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如:#pragmawarning(push)#pragmawarning(disable:4705)#pragmawarning(disable:4706)#pragmawarning(disable:4707)//.......#pragmawarning(pop)在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。.7,#pragmacomment#pragmacomment(...)该指令将一个注释记录放入一个对象文件或可执行文件中。常用的lib关键字,可以帮我们连入一个库文件。比如:#pragmacomment(lib,"user32.lib")该指令用来将user32.lib库文件加入到本工程中。linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中设置的链接选项,你可以指定/include选项来强制包含某个对象,例如:#pragmacomment(linker,"/include:__mySymbol").8,#pragmapack这里重点讨论内存对齐的问题和#pragmapack()的使用方法。什么是内存对齐?先看下面的结构:structTestStruct1{charc1;shorts;charc2;inti;};假设这个结构的成员在内存中是紧凑排列的,假设c1的地址
front=current;
current=current->next;
new=(NODE*)malloc(sizeof(NODE));
new->data=x;
new->next=current;
if(current==h)/*判断是否是要插在表头*/
h=new;
elsefront->next=new;
删除结点
NODE*delete(NODE*h,intx)
NODE*q,*p=h;
while(p!
=NULL&&(p->data!
=x))
p=p->next;
if(p->data==x)/*找到了要删的结点*/
if(p==h)/*判断是否要删表头*/
h=h->next;
elseq->next=p->next;
free(p);/*释放掉已删掉的结点*/
文件包含
文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编
译,结果将生成一个目标文件。
C语言提供#include命令来实现文件包含的操作,它实际是
宏替换的延伸,有两种格式:
格式1:
其中,filename为要包含的文件名称,用尖括号括起来,也称为头文件,表示预处理到
系统规定的路径中去获得这个文件(即C编译系统所提供的并存放在指定的子目录下的头
文件)。
找到文件后,用文件内容替换该语句。
格式2:
#include“filename”
其中,filename为要包含的文件名称。
双引号表示预处理应在当前目录中查找文件名为
filename的文件,若没有找到,则按系统指定的路径信息,搜索其他目录。
找到文件后,用
文件内容替换该语句。
需要强调的一点是:
#include是将已存在文件的内容嵌入到当前文件中。
另外关于#include的路径也有点要说明:
include支持相对路径,格式如trackant(蚁迹寻
踪)所写:
.代表当前目录,..代表上层目录。
代码书写
首先我想说明我本文阐述的是纯粹从美学的角度来写出代码,而非技术、逻辑等。
以下为写出漂亮代码的七种方法:
1尽快结束if语句
例如下面这个JavaScript语句,看起来就很恐怖:
1functionfindShape(flags,point,attribute,list){
2 if(!
findShapePoints(flags,point,attribute)){
3 if(!
doFindShapePoints(flags,point,attribute)){
4 if(!
findInShape(flags,point,attribute)){
5 if(!
findFromGuide(flags,point){
6 if(list.count()>0&&flags==1){
7 doSomething();
8 }
9 }
10 }
11 }
12 }
13 }
但如果这么写就好看得多:
2 if(findShapePoints(flags,point,attribute)){
3 return;
4 }
5
6 if(doFindShapePoints(flags,point,attribute)){
7 return;
9
10 if(findInShape(flags,point,attribute)){
11 return;
13
14 if(findFromGuide(flags,point){
15 return;
16 }
17
18 if(!
(list.count()>0&&flags==1)){
19 return;
20 }
21
22 doSomething();
23
24}
你可能会很不喜欢第二种的表述方式,但反映出了迅速返回if值的思想,也可以理解为:
避免不必要的else陈述。
2如果只是简单的布尔运算(逻辑运算),不要使用if语句
例如:
1functionisStringEmpty(str){
2 if(str===""){
3 returntrue;
5 else{
6 returnfalse;
7 }
8}
可以写为:
2 return(str==="");
3}
3使用空白,这是免费的
1functiongetSomeAngle(){
2 //Somecodeherethen
3 radAngle1=Math.atan(slope(center,point1));
4 radAngle2=Math.atan(slope(center,point2));
5 firstAngle=getStartAngle(radAngle1,point1,center);
6 secondAngle=getStartAngle(radAngle2,point2,center);
7 radAngle1=degreesToRadians(firstAngle);
8 radAngle2=degreesToRadians(secondAngle);
9 baseRadius=distance(point,center);
10 radius=baseRadius+(lines*y);
11 p1["x"]=roundValue(radius*Math.cos(radAngle1)+center["x"]);
12 p1["y"]=roundValue(radius*Math.sin(radAngle1)+center["y"]);
13 pt2["x"]=roundValue(radius*Math.cos(radAngle2)+center["y"]);
14 pt2["y"]=roundValue(radius*Math.sin(radAngle2)+center["y");
15 //Nowsomemorecode
16}
很多开发者不愿意使用空白,就好像这要收费一样。
我在此并非刻意地添加空白,粗鲁地打断代码的连贯性。
在实际编写代码的过程中,会很容易地发现在什么地方加入空白,这不但美观而且让读者易懂,如下:
6 firstAngle=getStartAngle(radAngle1,point1,center);
7 secondAngle=getStartAngle(radAngle2,point2,center);
8
9 radAngle1=degreesToRadians(firstAngle);
10 radAngle2=degreesToRadians(secondAngle);
11
12 baseRadius=distance(point,center);
13 radius=baseRadius+(lines*y);
14
15 p1["x"]=roundValue(radius*Math.cos(radAngle1)+center["x"]);
16 p1["y"]=roundValue(radius*Math.sin(radAngle1)+center["y"]);
18 pt2["x"]=roundValue(radius*Math.cos(radAngle2)+center["y"]);
19 pt2["y"]=roundValue(radius*Math.sin(radAngle2)+center["y");
20 //Nowsomemorecode
21}
4不要使用无谓的注释
无谓的注释让人费神,这实在很讨厌。
不要标出很明显的注释。
在以下的例子中,每个人都知道代码表达的是“studentsid”,因而没必要标出。
1functionexistsStudent(id,list){
2 for(i=0;i3 student=list[i];45 //Getthestudent'sid6 thisId=student.getId();78 if(thisId===id){9 returntrue;10 }11 }12 returnfalse; 13}5不要在源文件中留下已经删除的代码,哪怕你标注了如果你使用了版本控制,那么你就可以轻松地找回前一个版本的代码。如果别人大费周折地读了你的代码,却发现是要删除的代码,这实在太恨人了。//functionthisReallyHandyFunction(){//someMagic();//someMoreMagic();//magicNumber=evenMoreMagic();// returnmagicNumber;//}6不要有太长的代码看太长的代码实在太费劲,尤其是代码本身的功能又很小。如下:1publicstaticEnumMapgetGroupCategoryDistribution(EnumMapsizes,intgroups){2 EnumMapcategoryGroupCounts=newEnumMap(Category.class);34 for(Categorycat:Category.values()){5 categoryGroupCounts.put(cat,getCategoryDistribution(sizes.get(cat),groups));6 }我并不是说非要坚持70个字符以内,但是一个比较理想的长度是控制在120个字符内。如果你把代码发布在互联网上,用户读起来就很困难。7不要在一个功能(或者函数内)有太多代码行我的一个老同事曾经说VisualC++很臭,因为它不允许你在一个函数内拥有超过10,000行代码。我记不清代码行数的上限,不知道他说的是否正确,但我很不赞成他的观点。如果一个函数超过了50行,看起来有多费劲你知道么,还有没完没了的if循环,而且你还的滚动鼠标前后对照这段代码。对我而言,超过35行的代码理解起来就很困难了。我的建议是超过这个数字就把一个函数代码分割成两个。#pragma预处理在所有的预处理指令中,#pragma指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。其格式一般为:#pragmapara其中para为参数,下面来看一些常用的参数。.1,#pragmamessagemessage参数:Message参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:#pragmamessage(“消息文本”)当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法#ifdef_X86#Pragmamessage(“_X86macroactivated!”)#endif当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_X86macroactivated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了.2,#pragmacode_seg另一个使用得比较多的pragma参数是code_seg。格式如:#pragmacode_seg(["section-name"[,"section-class"]])它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。.3,#pragmaonce#pragmaonce(比较常用)只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VisualC++6.0中就已经有了,但是考虑到兼容性并没有太多的使用它。.4,#pragmahdrstop#pragmahdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragmastartup指定编译优先级,如果使用了#pragmapackage(smart_init),BCB就会根据优先级的大小先后编译。.5,#pragmaresource#pragmaresource"*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。.6,#pragmawarning#pragmawarning(disable:450734;once:4385;error:164)等价于:#pragmawarning(disable:450734)//不显示4507和34号警告信息#pragmawarning(once:4385)//4385号警告信息仅报告一次#pragmawarning(error:164)//把164号警告信息作为一个错误。同时这个pragmawarning也支持如下格式:#pragmawarning(push[,n])#pragmawarning(pop)这里n代表一个警告等级(1---4)。#pragmawarning(push)保存所有警告信息的现有的警告状态。#pragmawarning(push,n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。#pragmawarning(pop)向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如:#pragmawarning(push)#pragmawarning(disable:4705)#pragmawarning(disable:4706)#pragmawarning(disable:4707)//.......#pragmawarning(pop)在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。.7,#pragmacomment#pragmacomment(...)该指令将一个注释记录放入一个对象文件或可执行文件中。常用的lib关键字,可以帮我们连入一个库文件。比如:#pragmacomment(lib,"user32.lib")该指令用来将user32.lib库文件加入到本工程中。linker:将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或者在开发环境中设置的链接选项,你可以指定/include选项来强制包含某个对象,例如:#pragmacomment(linker,"/include:__mySymbol").8,#pragmapack这里重点讨论内存对齐的问题和#pragmapack()的使用方法。什么是内存对齐?先看下面的结构:structTestStruct1{charc1;shorts;charc2;inti;};假设这个结构的成员在内存中是紧凑排列的,假设c1的地址
3 student=list[i];
4
5 //Getthestudent'sid
6 thisId=student.getId();
7
8 if(thisId===id){
9 returntrue;
12 returnfalse;
13}
5不要在源文件中留下已经删除的代码,哪怕你标注了
如果你使用了版本控制,那么你就可以轻松地找回前一个版本的代码。
如果别人大费周折地读了你的代码,却发现是要删除的代码,这实在太恨人了。
//functionthisReallyHandyFunction(){
//someMagic();
//someMoreMagic();
//magicNumber=evenMoreMagic();
// returnmagicNumber;
//}
6不要有太长的代码
看太长的代码实在太费劲,尤其是代码本身的功能又很小。
如下:
1publicstaticEnumMapgetGroupCategoryDistribution(EnumMapsizes,intgroups){
2 EnumMapcategoryGroupCounts=newEnumMap(Category.class);
3
4 for(Categorycat:
Category.values()){
5 categoryGroupCounts.put(cat,getCategoryDistribution(sizes.get(cat),groups));
6 }
我并不是说非要坚持70个字符以内,但是一个比较理想的长度是控制在120个字符内。
如果你把代码发布在互联网上,用户读起来就很困难。
7不要在一个功能(或者函数内)有太多代码行
我的一个老同事曾经说VisualC++很臭,因为它不允许你在一个函数内拥有超过10,000行代码。
我记不清代码行数的上限,不知道他说的是否正确,但我很不赞成他的观点。
如果一个函数超过了50行,看起来有多费劲你知道么,还有没完没了的if循环,而且你还的滚动鼠标前后对照这段代码。
对我而言,超过35行的代码理解起来就很困难了。
我的建议是超过这个数字就把一个函数代码分割成两个。
#pragma预处理
在所有的预处理指令中,#pragma指令可能是最复杂的了,它的作用是设定编译器的
状态或者是指示编译器完成一些特定的动作。
#pragma指令对每个编译器给出了一个方法,
在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。
依据定义,编译
指示是机器或操作系统专有的,且对于每个编译器都是不同的。
其格式一般为:
#pragmapara
其中para为参数,下面来看一些常用的参数。
.1,#pragmamessage
message参数:
Message参数是我最喜欢的一个参数,它能够在编译信息输出窗
口中输出相应的信息,这对于源代码信息的控制是非常重要的。
其使用方法为:
#pragmamessage(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有
正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。
假设我们希望判
断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef_X86
#Pragmamessage(“_X86macroactivated!
”)
#endif
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
X86macroactivated!
”。
我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了
.2,#pragmacode_seg
另一个使用得比较多的pragma参数是code_seg。
格式如:
#pragmacode_seg(["section-name"[,"section-class"]])
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。
.3,#pragmaonce
#pragmaonce(比较常用)
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在
VisualC++6.0中就已经有了,但是考虑到兼容性并没有太多的使用它。
.4,#pragmahdrstop
#pragmahdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。
BCB可以
预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,
所以使用这个选项排除一些头文件。
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。
你可以用#pragmastartup指定编译优先级,如果使用了#pragmapackage(smart_init),BCB
就会根据优先级的大小先后编译。
.5,#pragmaresource
#pragmaresource"*.dfm"表示把*.dfm文件中的资源加入工程。
*.dfm中包括窗体
外观的定义。
.6,#pragmawarning
#pragmawarning(disable:
450734;once:
4385;error:
164)
等价于:
450734)//不显示4507和34号警告信息
#pragmawarning(once:
4385)//4385号警告信息仅报告一次
#pragmawarning(error:
164)//把164号警告信息作为一个错误。
同时这个pragmawarning也支持如下格式:
#pragmawarning(push[,n])
#pragmawarning(pop)
这里n代表一个警告等级(1---4)。
#pragmawarning(push)保存所有警告信息的现有的警告状态。
#pragmawarning(push,n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragmawarning(pop)向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。
#pragmawarning(push)
4705)
4706)
4707)
//.......
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
.7,#pragmacomment
#pragmacomment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。
比如:
#pragmacomment(lib,"user32.lib")
该指令用来将user32.lib库文件加入到本工程中。
linker:
将一个链接选项放入目标文件中,你可以使用这个指令来代替由命令行传入的或
者在开发环境中设置的链接选项,你可以指定/include选项来强制包含某个对象,例如:
#pragmacomment(linker,"/include:
__mySymbol")
.8,#pragmapack
这里重点讨论内存对齐的问题和#pragmapack()的使用方法。
什么是内存对齐?
先看下面的结构:
structTestStruct1
charc1;
shorts;
charc2;
inti;
假设这个结构的成员在内存中是紧凑排列的,假设c1的地址
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1