单片机C语言编程基础Word格式文档下载.docx
《单片机C语言编程基础Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《单片机C语言编程基础Word格式文档下载.docx(33页珍藏版)》请在冰豆网上搜索。
printf("
string=%sfloat=%f\n"
str,f);
return0;
}
链表
链表是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;
='
next=h;
/*不断地把head往前挪*/
h=p;
用递归实现链表逆序输出:
voidoutput(NODE*h)
if(h!
=NULL)
output(h->
next);
%c"
h->
data);
插入结点
插入结点(已有升序的链表):
NODE*insert(NODE*h,intx)
NODE*new,*front,*current=h;
while(current!
=NULL&
&
(current->
data<
x))/*查找插入的位置*/
front=current;
current=current->
next;
new=(NODE*)malloc(sizeof(NODE));
new->
data=x;
next=current;
if(current==h)/*判断是否是要插在表头*/
h=new;
elsefront->
next=new;
删除结点
NODE*delete(NODE*h,intx)
NODE*q,*p=h;
while(p!
(p->
data!
=x))
p=p->
if(p->
data==x)/*找到了要删的结点*/
if(p==h)/*判断是否要删表头*/
h=h->
next=p->
free(p);
/*释放掉已删掉的结点*/
文件包含
文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编
译,结果将生成一个目标文件。
C语言提供#include命令来实现文件包含的操作,它实际是
宏替换的延伸,有两种格式:
格式1:
filename>
其中,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
doFindShapePoints(flags,point,attribute)){
4
findInShape(flags,point,attribute)){
5
findFromGuide(flags,point){
6
if(list.count()>
0&
flags==1){
7
doSomething();
8
}
9
10
11
12
}
13
但如果这么写就好看得多:
if(findShapePoints(flags,point,attribute)){
return;
5
if(doFindShapePoints(flags,point,attribute)){
9
if(findInShape(flags,point,attribute)){
13
14
if(findFromGuide(flags,point){
15
16
17
18
if(!
(list.count()>
flags==1)){
19
20
21
22
23
24}
你可能会很不喜欢第二种的表述方式,但反映出了迅速返回if值的思想,也可以理解为:
避免不必要的else陈述。
2如果只是简单的布尔运算(逻辑运算),不要使用if语句
例如:
1functionisStringEmpty(str){
if(str==="
"
){
returntrue;
else{
returnfalse;
8}
可以写为:
return(str==="
);
3}
3使用空白,这是免费的
1functiongetSomeAngle(){
//Somecodeherethen
radAngle1=Math.atan(slope(center,point1));
radAngle2=Math.atan(slope(center,point2));
firstAngle=getStartAngle(radAngle1,point1,center);
secondAngle=getStartAngle(radAngle2,point2,center);
radAngle1=degreesToRadians(firstAngle);
radAngle2=degreesToRadians(secondAngle);
baseRadius=distance(point,center);
radius=baseRadius+(lines*y);
p1["
x"
]=roundValue(radius*Math.cos(radAngle1)+center["
]);
y"
]=roundValue(radius*Math.sin(radAngle1)+center["
pt2["
]=roundValue(radius*Math.cos(radAngle2)+center["
]=roundValue(radius*Math.sin(radAngle2)+center["
//Nowsomemorecode
16}
很多开发者不愿意使用空白,就好像这要收费一样。
我在此并非刻意地添加空白,粗鲁地打断代码的连贯性。
在实际编写代码的过程中,会很容易地发现在什么地方加入空白,这不但美观而且让读者易懂,如下:
8
11
14
21}
4不要使用无谓的注释
无谓的注释让人费神,这实在很讨厌。
不要标出很明显的注释。
在以下的例子中,每个人都知道代码表达的是“studentsid”,因而没必要标出。
1functionexistsStudent(id,list){
for(i=0;
i<
list.length;
i++){
student=list[i];
4
//Getthestudent'
sid
thisId=student.getId();
7
if(thisId===id){
13}
5不要在源文件中留下已经删除的代码,哪怕你标注了
如果你使用了版本控制,那么你就可以轻松地找回前一个版本的代码。
如果别人大费周折地读了你的代码,却发现是要删除的代码,这实在太恨人了。
//functionthisReallyHandyFunction(){
//someMagic();
//someMoreMagic();
//magicNumber=evenMoreMagic();
//
returnmagicNumber;
//}
6不要有太长的代码
看太长的代码实在太费劲,尤其是代码本身的功能又很小。
如下:
1publicstaticEnumMap<
Category,IntPair>
getGroupCategoryDistribution(EnumMap<
Category,Integer>
sizes,intgroups){
EnumMap<
categoryGroupCounts=newEnumMap<
Category,IntPair>
(Category.class);
3
for(Categorycat:
Category.values()){
categoryGroupCounts.put(cat,getCategoryDistribution(sizes.get(cat),groups));
我并不是说非要坚持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)
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的地址