单片机C语言编程基础.docx

上传人:b****5 文档编号:3423233 上传时间:2022-11-22 格式:DOCX 页数:33 大小:222.60KB
下载 相关 举报
单片机C语言编程基础.docx_第1页
第1页 / 共33页
单片机C语言编程基础.docx_第2页
第2页 / 共33页
单片机C语言编程基础.docx_第3页
第3页 / 共33页
单片机C语言编程基础.docx_第4页
第4页 / 共33页
单片机C语言编程基础.docx_第5页
第5页 / 共33页
点击查看更多>>
下载资源
资源描述

单片机C语言编程基础.docx

《单片机C语言编程基础.docx》由会员分享,可在线阅读,更多相关《单片机C语言编程基础.docx(33页珍藏版)》请在冰豆网上搜索。

单片机C语言编程基础.docx

单片机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);

程序例:

#include

intmain(void)

{

floatf;

char*str="12345.67";

f=atof(str);

printf("string=%sfloat=%f\n",str,f);

return0;

}

###############################################################################

链表

链表是C语言中另外一个难点。

牵扯到结点、动态分配空间等等。

用结构作为

链表的结点是非常适合的,例如:

structnode

{

intdata;

structnode*next;

};

其中next是指向自身所在结构类型的指针,这样就可以把一个个结点相连,构

成链表。

链表结构的一大优势就是动态分配存储,不会像数组一样必须在定义时确定大

小,造成不必要的浪费。

用malloc和free函数即可实现开辟和释放存储单元。

其中,malloc的参数多用sizeof运算符计算得到。

链表的基本操作有:

正、反向建立链表;输出链表;删除链表中结点;在链表中

插入结点等等,都是要熟练掌握的,初学者通过画图的方式能比较形象地理解建

立、插入等实现的过程。

typedefstructnode

{

chardata;

structnode*next;

}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*create()

{

charch='a';

NODE*p,*h=NULL;

while(ch<='z')

{

p=(NODE*)malloc(sizeof(NODE));

p->data=ch;

p->next=h;/*不断地把head往前挪*/

h=p;

ch++;

}

returnh;

}

用递归实现链表逆序输出:

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   }

5

6   if(doFindShapePoints(flags,point,attribute)){

7       return;

8   }

9

10   if(findInShape(flags,point,attribute)){

11       return;

12   }

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;

4   }

5   else{

6       returnfalse;

7   }

8}

可以写为:

1functionisStringEmpty(str){

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}

很多开发者不愿意使用空白,就好像这要收费一样。

我在此并非刻意地添加空白,粗鲁地打断代码的连贯性。

在实际编写代码的过程中,会很容易地发现在什么地方加入空白,这不但美观而且让读者易懂,如下:

1functiongetSomeAngle(){

2   //Somecodeherethen

3   radAngle1=Math.atan(slope(center,point1));

4   radAngle2=Math.atan(slope(center,point2));

5

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"]);

17

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;i

3      student=list[i];

4

5      //Getthestudent'sid

6      thisId=student.getId();

7

8      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);

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)

等价于:

#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的地址

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 学科竞赛

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1