单片机C语言编程基础.docx
- 文档编号:3423233
- 上传时间:2022-11-22
- 格式:DOCX
- 页数:33
- 大小:222.60KB
单片机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);
程序例:
#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不要有太长的代码 看太长的代码实在太费劲,尤其是代码本身的功能又很小。 如下: 1publicstaticEnumMap 2 EnumMap 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的地址
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 单片机 语言 编程 基础