1、嵌入式单片机原理及应用燕山大学电气工程学院自动化系燕山大学电气工程学院自动化系 1 ARM嵌入式系统概述2 STM32单片机结构和最小系统 3 基于标准外设库的C语言程序设计基础 4 STM32通用输入输出GPIO5 STM32外部中断6 STM32通用定时器7 STM32通用同步/异步收发器USART8 直接存储器存取DMA9 STM32的模数转换器ADC10 STM32的集成电路总线I2C11 STM32的串行外设接口SPI第第3章章 基于标准外设库的基于标准外设库的C语言程序设计基础语言程序设计基础C语言的特点语言的特点1、语言简单、紧凑、灵活。(32个关键字,9种控制语句)2、运算符丰
2、富(34种表达式)3、数据结构丰富4、具有结构化的控制语句5、目标代码质量高,程序执行效率高。6、可移值性好7、兼有低级语言和高级语言的特点3.1 嵌入式嵌入式C语言基础语言基础 3.1 嵌入式嵌入式C语言基础语言基础 每个C程序通常分为两个文件,一个文件用于保存程序的声明,称为头文件,以“.h”为后缀。另一个文件用于保存程序的实现,称为源文件,以“.c”为后缀。如果一个工程中头文件数目较多,通常将头文件和源文件分别保存在不同的目录以便于维护。例如可以将头文件保存于inc目录,源文件保存于src目录。版权和版本声明版权和版本声明/* Copyright (c) 2014,燕山大学电气工程学院*
3、 All rights reserved.* 文件名称:filename.h* 摘 要:简要描述本文件的内容* 当前版本:1.1* 作 者:输入作者(或修改者)名字* 完成日期:2014年7月20日* 取代版本:1.0 * 原作者 :输入原作者(或修改者)名字* 完成日期:2014年1月10日*/头文件结构头文件结构头文件版权和版本声明、预处理块和函数声明头文件版权和版本声明、预处理块和函数声明/ 版权和版本声明版权和版本声明#ifndef_GRAPHICS_H_#define_GRAPHICS_H_#include #include “myheader.h” / 引用非标准库的头文件void
4、 Function1();/ 全局函数声明#endif源文件结构源文件结构源文件版权和版本声明、头文件的引用和程序的实现体源文件版权和版本声明、头文件的引用和程序的实现体/ 版权和版本声明版权和版本声明#include “graphics.h”/ 全局函数的实现体void Function1()简单的简单的C程序介绍程序介绍例1:最简单的程序#include /包含头文件 int main(void)/*主函数*/printf(“this is a c program!n”); /*输出了一句话*/例例2 求两个数之和求两个数之和#include /包含头文件 int main(void)/*
5、主函数*/ int a,b,sum;/*定义变量*/a=100;b=200; /*给变量赋值*/sum=a+b/*求a与b的和*/printf(“sum=%dn”,sum);/*输出结果*/ scanf(“%d%d”,&a,&b);例例3 求两个数中的最大数求两个数中的最大数#include /包含头文件int max(int x,int y); /*定义max函数,函数值为整型,x,y为型参,整型*/ int z; /*函数内用到内部变量z,也要加以定义*/ if(xy) z=x; /*比较x,y的大小,如果x大于y,则执行z=x*/else z=y; /*否则执行z=y*/return(z
6、); /*将z的值返回,通过max带回调用处*/ int main(void)/*主函数*/int a,b,c; /*定义变量*/ scanf(“%d%d”,&a,&b); /*输入变量a,b的值*/ c=max(a,b); /*调用函数,将得到的值赋给c*/ printf(“max=%dn”,c); /*输出c的值*/C程序的结构特点和书写格式程序的结构特点和书写格式1、函数是组成C程序的基本结构2、一个函数由两部分组成:函数说明部分函数体函数体:说明部分 执行部分 3、一个程序总是从main函数开始执行4、语句以分号;结束5、书写格式自由6、用/*/做注释字符集字符集什么是字符集在C语言程
7、序中允许出现的所有基本字符的组合称为C语言的字符集。字符集分类(1) 大小写英文字母(52个)(2) 数字符号(10个)(3) 键盘符号(33个)(4) 转义字符C语言的基本元素语言的基本元素1、符号集(字符集)C语言使用的基本符号共有如下5种:(1)大写字母:A-Z(2)小写字母:a-z(3)阿拉伯数字:0-9(4)下划线:_(5)标点符号和运算符标识符标识符什么是标识符什么是标识符标识符是用户自定义的一种字符序列,通常用来表示程序中需要辨认的对象名称。标识符的命名规则标识符的命名规则标识符是由字符或下划线开头的字母、数字、下划线组成的一串符号;保留字不能作为标识符。“_”可以作为标识符的第
8、一个字母,但是往往用于库代码正确标识符正确标识符:sum i a2 a_2 _a2 _a_2错误的标识符错误的标识符:2a a? c.g a-2 if whileC语言的数据类型语言的数据类型数据是操作的对象,数据类型是指数据的内在表现形式(代码、存储、运算)C语言程序的数据类型如下:空类型数据类型基本类型数据类型指针类型整型int实型(浮点型)字符型char单精度flaot双精度double数组结构体共用体枚举型派生类型关键字关键字意义意义char声明字符型变量或函数数double声明双精度变量或函数据enum声明枚举类型类float声明浮点型变量或函数型int声明整型变量或函数long声明
9、长整型变量或函数short声明短整型变量或函数signed声明有符号类型变量或函数struct声明结构体变量或函数union声明共用体(联合)数据类型unsigned声明无符号类型变量或函数void声明函数无返回值或无参数,声明空类型指针C语言的数据类型语言的数据类型常量常量什么是常量又称为字面量,表述常数常量类型整型常量实型常量字符常量字符串常量常量和变量常量和变量一、常量和符号常量1、常量:在程序运行过程中, 其值不能被改变的量常量区分为不同类型(表现形式)如:12、3.2、a二、变量1、变量:其值是可以改变的量,它用标识符(变量名)来表示,在内存中占据一定的存储单元。2、变量的定义方法类
10、型符标识符3、注意:1.见字知意2.先定义后使用3.习惯:符号常量大写,变量名小写如:int a,b,max; char c1,c2,s; float x,y,z; double i,j;变量变量变量数据类型数据类型类型符号占用字节数数据类型类型符号占用字节数整型int4无符号长整型unsigned long4短整型short2单精度实型float4长整型long4双精度实型double8无符号整型unsigned int4字符型char1无符号短整型unsigned short2变量存储类型变量存储类型存储类型存储类型符存储地点自动型auto内存堆栈区寄存器型registerCPU的通用寄存
11、器静态型static内存数据区外部参照型extern变量的定义和初始化变量的定义和初始化变量的初始化变量在定义时就要初始化变量赋初值的语句格式存储类型符 数据类型符 变量名=初值;结构型的定义结构型的定义定义语句struct 结构名称 数据类型1 成员1名; 数据类型2 成员2名; . . 数据类型n 成员n名;结构的本质是不同类型元素的集合结构型变量的定义和引用结构型变量的定义和引用结构型变量的定义方法:先定义结构型、后定义变量struct student int id; char name20; int age;struct student x , y;结构型变量成员的引用结构型变量成员的
12、引用引用方法结构型变量成员的引用方法引用格式:结构型变量名.成员名结构型变量成员地址的引用方法引用格式:&结构型变量名.成员名结构型变量地址的引用方法引用格式:&结构型变量名指向结构型数据的指针变量的定义和引用指向结构型数据的指针变量的定义和引用指向结构型变量的指针定义方法:同结构型变量的定义方法,区别在于定义时变量名前面加*。引用方法方法1: (指针变量).成员名方法2: 指针变量成员名基础练习:基础练习:输入3个学生信息,并输出!(使用两种引用方式,结构体变量和结构体指针)学生结构体定义如下:struct student int id; char name20; int age;编程练习编
13、程练习学生管理系统:学生管理系统可以实现学生信息的录入,查询,注销等功能。定义学生的结构体(以后可以按照具体情况增加个人信息内容)struct student int id; char name20; int age;C语言中的运算符语言中的运算符运算符运算符什么是运算符用来表示各种运算的符号称为运算符。运算符特点运算符必须有运算对象运算对象,运算对象都有规定的数据类型数据类型,同时运算结果也有确定的数据类型。每个运算符都有自己特定的运算规则运算规则。当表达式中出现多个运算符时则必须考虑运算符的优先级优先级。同级别的运算符还规定了结合性结合性。算术运算符算术运算符运算符名称优先级结合性+、-取
14、正、取负2自右向左*、/、%乘、除、取余数3自左向右+、-加、减4自左向右增增1、减、减1运算符运算符有哪些运算符号j = i+; 相当于 j = I; i= i+1;j = +i; 相当于 i = i +1; j = i; 运算符名称优先级结合性+增12自右向左- -减1关系运算符关系运算符运算符名称优先级结合性=、 、= 、大于等于、大于、小于等于、小于6自左向右=、!=等于、不等于7自左向右关系运算符要点关系运算符要点关系运算符可以用来比较两个数值型数据的大小,也可以比较两个字符型数据的大小。关系运算符的运算结果是逻辑值,若为“真”用整数1表示,若为“假”用整数0表示。逻辑运算符逻辑运算
15、符运算符名称优先级结合性!逻辑非2自右向左&逻辑与11自左向右|逻辑或12自左向右逻辑运算符的特点逻辑运算符的特点逻辑运算符运算对象是数值型或字符型等;如果是非0表示逻辑真,0表示逻辑假。运算结果是逻辑值,如运算结果为真用1表示,若为假则用0表示。逻辑运算逻辑运算用“&”对两个表达式进行计算时,若第1个表达式的值为“假”,则与第2个表达式的值无关,结果肯定为“假”,所以C语言规定此时第2个表达式不再计算用“| ”对两个表达式进行计算时,若第1个表达式的值为“真”,则与第2个表达式的值无关,结果肯定为“真”,所以C语言规定此时第2个表达式不再计算赋值运算符和赋值表达式赋值运算符和赋值表达式一、赋
16、值运算符“=”用法:变量=表达式作用:将表达式的值赋给变量如:a=5;ave=(a+b)/10;二、赋值结果和类型转换1、实型数据(包括单、双精度)赋给整型变量时,舍弃实数的小数部分。2、整型数据赋给单、双精度变量时,数值不变,但以浮点数形式存储到变量中#include “stdio.h” void main() int i; float f; i=1.23; f=45; printf(“%d %fn”,i,f);结果:i=1 f=45.000000逗号运算符和逗号表达式逗号运算符和逗号表达式形式:表达式1,表达式2,表达式n作用:用于连接表达式 如:3+5,6-5计算过程:1、求解表达式12
17、、再求解表达式2,表达式n3、整个逗号表达式的值是表达式n的值.一个逗号表达式又可以与另一个表达式组成一个新的逗号表达式。如;(a=3*5,a*4),a+5 x=a=3,6*a注意:并不是任何地方出现的 逗号都是逗号运算符 如:printf(“%d,%dn”,a,b)条件运算符条件运算符该运算符是三目运算符,其三个运算对象是表达式(e1?e2:e3);运算规则是如果e1表达式为“真”,取e2表达式的值,否则取e3表达式的值。位运算符位运算符什么是位运算是一种对运算对象按二进制位进行操作的运算。位运算的特点位运算不允许只操作其中的某一位,而是对整个数据按二进制位进行运算;位运算的对象只能是整型数
18、据(包括字符型),运算结果仍是整型数据位运算符分为位逻辑运算符、位移位运算符、位自反赋值运算符三种位运算符位运算符运算符名称优先级结合性位非2自右向左&位与8自左向右异或9自左向右|位或10自左向右位移位运算符位移位运算符运算符名称优先级结合性右移自左向右位运算符位运算符按位与 & 0&0=0; 0&1=0; 1&0=0; 1&1=1 按位或 | 0|0=0; 0|1=1; 1|0=1; 1|1=1 按位异或 00=0; 01=1; 10=1; 11=0按位取反 0=1; 1=0;表达式表达式什么是表达式用运算符将运算对象连接形成的式子就是表达式。表达式的特点每个表达式都可以按照其中运算符的优
19、先级和运算规则依次对运算对象进行运算,最终获得一个数据,该数据称为表达式的值。表达式值的数据类型就称为表达式的数据类型。表达式的分类主要有六种:算术表达式、关系表达式、逻辑表达式、条件表达式、赋值表达式和逗号表达式。表达式类型转换方法表达式类型转换方法表达式计算中数据类型的自动转换原则特点:参加运算的各个数据都转换成数据长度最长的数据类型,然后计算。计算结果值当然就是数据长度最长的数据类型例如:设i为int,f为float,d为double,e为long则10+a+i*f-d/e的结果是double。运算结果存入变量时数据类型的自动转换原则特点:先将运算结果的数据类型自动转换成变量的数据类型,
20、然后再赋予该变量不同类型数据间的混合运算不同类型数据间的混合运算整型、实型(包括单、双精度)、字符型数据间可以混合运算。如:10+a+12.3-4.56*x是合法的运算时,不同类型的数据要先转换成同一类型,然后进行运算。转换规则如下:floatdoublelongunsignedintchar,short高低float型数据在运算时一律先转换成double型不同类型进行混合运算时,按照类型级别由低到高的顺序转换字符型和short型在运算时一律转换成int型分支结构程序设计分支结构程序设计关键字关键字意义意义for循环语句控do循环语句的循环体制while循环语句的循环条件语break跳出当前循
21、环句 continue结束当前循环,开始下一轮循环if条件语句else条件语句否定分支(与if连用)goto无条件跳转语句switch开关语句case开关语句分支default开关语句中的其它分支return返回语句在C语言中,分支结构主要是利用if语句来实现的,if语句是对给定的条件进行判断,然后决定执行某个分支。C语言的if语句有3种形式:if语句if else语句else if语句If语句语句条件执行一般形式为:If(表达式)语句含义:如果表达式的值为真,则执行其后的语句;否则不执行该语句。语句表达式真(非0)假(0)If(x0)printf(“hello!”);例例:输入两个数,输出其
22、中较大的数输入两个数,输出其中较大的数main()int a,b,max;printf(“n input two numbers:”);scanf(“%d%d”,&a,&b);max=a;if(maxb)max=a;min=b;elsemax=b;min=a;if(maxc)min=c;printf(“max=%dt min=%dn”,max,min);else-if语句语句前面两种形式的if语句一般都用于两个分支情况。在实际程序设计时经常需要进行多分支处理,C语言中进行多分支处理时,可采用else-if语句实现。阶梯式的if语句一般形式 if(表达式表达式1)语句语句1; elseif(表达
23、式表达式2)语句语句2; elseif(表达式表达式3)语句语句3; elseif(表达式表达式m)语句语句m; else语句语句m+1;含义:依次判断各个表达式的值,当出现某个值为真时,则执行其对应的语句,然后跳出else-if结构。如果所有的表达式的值均为假,则执行语句m+1,然后执行后续程序。表达式1语句1真真真真表达式2表达式3表达式4语句2语句3语句2语句2假假假假例:根据键盘输入字符的类型并输出相应的信息例:根据键盘输入字符的类型并输出相应的信息#include main()char c;printf(input a character:);c=getchar(); if(c=0&
24、c=a&c=A&cb)printf(“ABn”);elseprintf(“Ab)max=a; elsemax=b;可以采用条件运算符实现: max=(ab)?a:b;条件运算符是一个3目运算符,即有3个参与运算的量。由条件运算符组成的表达式称为条件表达式:表达式1?表达式2:表达式3使用条件表达式时,还应注意以下几点:(1)条件运算符的运算优先级低于关系运算符和算术运算符,但高于赋值运算符。(2)条件运算符?和:是一对运算符,不能分开单独使用。(3)条件运算符的结合方向是自右至左。如:ab?a:cd?c:d例例:比较两个数的大小并输出较大值比较两个数的大小并输出较大值 main( ) int
25、a,b;printf(“n input two numbers:”);scanf(“%d%d”,&a,&b);printf(“max=%d”,ab?a:b); switch-case语句语句在实际中经常需要处理多分支问题,如统计学生成绩分布等,虽然可以用else-if和嵌套if语句来实现多分支程序设计,但是当分支较多时,程序变得冗长且可读性低。在C语言中提供了直接处理多分支的方法:switch-case语句。一、格式: switch(表达式)case常量表达式1:语句1;case常量表达式2:语句2;case常量表达式n:语句n;default :语句n+1;二、执行过程1.计算表达式的值2.
26、若与常量表达式n值一致,则从该语句开始执行;直到遇到break语句或switch语句的3、若与任何常量表达式值均不一致时,则执行default语句或执行后续语句其中:表达式可以是整型、字符型、枚举型常量表达式必须与表达式类型一致(整型与字符型通用)常量表达式中的数据仅起语句标号作用,不作求值判断常量表达式的值必须是唯一的,没有先后顺序多个case语句可以共用一组执行语句循环结构程序设计循环结构程序设计内容提要:循环就是在满足一定条件时重复执行一段程序l 概述l 构成循环的语句while、do-while、for、if-gotol 循环结构的比较l break语句和continue语句While
27、语句语句用while语句实现循环while语句的一般形式:while (表达式) 语句;表达式语句下一语句假(0)真(非0)表达式语句 当表达式的值为真(非0)时,执行其中的内嵌语句(循环体),然后回过头来再判断表达式的值,如此重复;当表达式为假(0)时结束循环。while语句的一般形式:while (表达式) 语句;如:k=1;while(k=100)s+=k;k+;循环控制表达式循环控制变量循环体注意:u 若循环体包含一条以上的语句,应以复合语句形式出现u 循环体,必须给循环控制变量赋值u 循环体中,必须有改变循环控制变量值的语句(使循环趋向结束的语句)u 循环体可以为空如:while(c
28、=getchar()!=A); 等价:c=getchar(); while(c!=A)c=getchar();编程练习:编程练习:打印100以内所有的奇数(不包括100)打印100以内所有的偶数(不包括100)打印100以内所有3的倍数,并统计个数。输入一个数,判断是几位数?(使用循环实现!)do-while循环循环do-while语句用来实现“直到型”循环,它的一般形式为:do语语句句While(表达式表达式);不可以省略切记切记!语句表达式下一语句假真NS图注意点与while相同for循环循环for循环的一般形式:for(表达式1;表达式2;表达式3)语句求解表达式1表达式2语句求解表达式
29、3下一语句假真求解表达式1表达式2语句求解表达式3l表达式1在进入循环之前求解(循环变量赋初值)l表达式3是循环体的一部分for循环的其他形式:for( ;表达式2; )语句表达式3;表达式1表达式1;表达式3表达式2为空值永远为真成为死循环变量赋初值这个分号不能移走相当于循环体的一部分可以移到语句后面来说明说明1、表达式1省略时,应在for前给循环变量赋初值。如:k=1;for(;k=3;k+) s+=k;2、表达式2省略时,不判断循环条件,将成为“死循环”,需要在循环体内引入break语句以退出循环3、表达式3省略时,循环体内应有使循环条件改变的语句如:for(k=1;k=3; )s+=k
30、;k+;4、同时省略表达式1和表达式3,只有表达式2,此时相当于while语句。如:k=1;k=1;for(;k=3;)while(k=3)s+=k;k+s+=k;k+5、表达式2一般是关系表达式或逻辑表达式,但也可以是数值表达式或字符表达式,只要其值不等于0就执行循体如: for(k=1;k-4;k+)s+=k;仅当k的值等于4的时候终止循环。k-4是数值表达式。求求100以内的奇数、偶数之和以内的奇数、偶数之和循环的嵌套循环的嵌套u 一个循环体内包含着另一个完整的循环结构,就称为循环嵌套u 内嵌的循环中又可以嵌套循环,从而构成多重循环。u 三种循环可以互相嵌套下面给出几种合法的嵌套形式:(
31、1)while嵌套while(2)do-while嵌套do-whilewhile()dowhile()dowhile();while();(3)for嵌套for(4)while嵌套do-whilefor(;)while()for(;)dowhile();(5)for嵌套while(6)do-while嵌套forfor(;)dowhile()for; while();循环嵌套的说明循环嵌套的说明l 嵌套的循环控制变量不能相同l 内循环变化快,快循环变化慢l 正确确定循环体l 循环控制变量常与求解的问题挂钩有有1、2、3、4个数字,能组成多少个互不个数字,能组成多少个互不相同且无重复数字的三位数?
32、都是多少相同且无重复数字的三位数?都是多少?main()int i,j,k;printf(n);for(i=1;i5;i+)*以下为三重循环*/ for(j=1;j5;j+) for (k=1;k5;k+) if (i!=k&i!=j&j!=k) /*确保i、j、k三位互不相同*/ printf(%d,%d,%dn,i,j,k); 几种循环的比较几种循环的比较一、关于循环的控制循环控制条件修改循环条件循环条件初始化break语句和语句和continue语句语句一、break语句用break语句可以结束switch结构和三种循环、for(n=1;n1000) break; printf(“%f”
33、,n);二、continue语句用continue语句可以结束本次循环,既忽略循环体中剩余语句。如:把100200之间不能被3整除的数输出本次循环结束循环体的剩余语句被忽略执行表达式3,进下一循环continue总是作if的内嵌语句通用转移语句通用转移语句goto语句语句Goto语句随意跳转到任意位置Goto语句一般配合标号语句使用Goto语句容易导致代码混乱精心设计的goto语句可以带来代码的高效Goto语句在嵌入式编程中用的很多3.1.3 C语言知识精编语言知识精编 关键词关键词关键字关键字意义意义存auto声明自动变量,缺省时一般为“auto”储extern声明变量是在其它文件中声明(也
34、可以看做是引用变量)类register声明寄存器变量型static声明静态变量const声明只读变量sizeof计算对象所占内存空间大小其typedef给数据类型取别名它volatile说明变量在程序执行中switch开关语句case开关语句分支default开关语句中的其它分支return返回语句3.1.3 C语言知识精编语言知识精编 预处理预处理预处理命令预处理命令意义意义#define宏定义#undef撤销已定义过的宏名#include使编译程序将另一源文件嵌入到带有#include的源文件中#if#if 的一般含义是:如果#if 后面的常量表达式为true,则编译它与#endif 之间
35、的代码,否则跳过这些代码。#else#endif 标识一个#if 块的结束。#elif#else 在# if 失败的情况下建立另一选择。#endif#elif 命令意义与else if 相同,它形成一个if else-if 阶梯状语句,可进行多种编译选择。#ifdef#ifndef用#ifdef 与#ifndef 命令分别表示“如果有定义”及“如果无定义”,是条件编译的另一种方法#line改变当前行数和文件名称,它们是在编译程序中预先定义的标识符,命令的基本形式为:#line numberfilename#error编译程序时,#error 就会生成一个编译错误提示消息,并停止编译#pragm
36、a它允许向编译程序传送各种指令,当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。“预处理”是指在对C语言源程序进行编译之前,要先对程序中的“宏定义、文件包含、条件编译”等特殊命令进行预先处理。宏定义:定义一个标识符来表示一个字符串,称为“宏定义”,该标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”的地方都用字符串去代换。宏定义分类:分为有参数宏定义和无参数宏定义两种。宏的定义不带参数的宏定义宏定义的宏定义的一般格式一般格式为:为:#define 文本串文本串 /*#define就是宏定义命令就是宏定义命令*/功能:功能:将宏名的值定义为指定的文本串。将宏名的值定义为指
37、定的文本串。在预处理时凡出现宏名的地方在预处理时凡出现宏名的地方,都用指定的文本串替换。都用指定的文本串替换。在预处理时将宏名替换成指定的文本串的过程称为在预处理时将宏名替换成指定的文本串的过程称为“宏展开宏展开”,或称为或称为“宏替换宏替换”。【例】 求圆周长、面积和相同半径圆球体积。#define PI 3.14159void main() float r,l,s,v; printf(“input radius:”); scanf(“%f”,&r); l=2.0*PI*r; s=PI*r*r; v=3.0/4*PI*r*r*r; printf(“l=%6.2fns=%6.2fnv=%6.2
38、fn”,l,s,v); 程序运行结果:程序运行结果:input radius:6l= 37.70s=113.10v=508.94注意:(1) 宏名为了与变量名区别一般用大写字母表示。宏名为了与变量名区别一般用大写字母表示。(2) 宏展开时只用文本串替换宏名,不作正确性检查。宏展开时只用文本串替换宏名,不作正确性检查。(3) 宏定义行末尾不能加分号。加分号,宏展时会当作文宏定义行末尾不能加分号。加分号,宏展时会当作文 本串字符一同代入。本串字符一同代入。 (4) 宏名的有效范围是:从定义位置开始到本文件结束宏名的有效范围是:从定义位置开始到本文件结束(5) 可以用可以用#undef命令终止宏定义
39、的作用域。命令终止宏定义的作用域。(6) 在宏定义时,可以引用已定义的宏名。在宏定义时,可以引用已定义的宏名。【例】在宏定义时引用已定义的宏名#define R 4.0#define PI 3.14159#define L 2*PI*R#define S PI*R*Rvoid main() printf(“L=%6.2fnS=%6.2fn”,L,S);运行运行结结果如下:果如下:L= 25.13S= 50.27【例】分析下列程序【例】分析下列程序#define OK 100void main() printf(OK); printf(n);运行运行结结果果为为: :OK为为什么不是什么不是10
40、0?带参数的宏定义带参数的宏定义格式如下:#define (参数表) 文本串功能:定义一个带参数的宏。例如:#define S(x,y) 3*x+2*yarea=S(3,2);宏展开:用实参从左到右逐个字符进行替换文本串中的形参字符。如图8.1所示。执行:area=S(3,2) 宏展开后变成:area=3*3+2*2; 【例】 求圆的面积。 #define PI 3.14159 #define S(r) PI*r*r void main() float a,area; a=3.6; area=S(a); printf(“area=%6.2fn”,area); 运行运行结结果:果:area= 4
41、0.72说说明:明:(1) 宏名与括弧之宏名与括弧之间间不能有空格。不能有空格。(2) 宏宏调调用用时时, ,实实参的个数必参的个数必须须与形参的个与形参的个数相同。数相同。(3) 带带参数的宏替参数的宏替换时换时不作不作语语法法检查检查。 。(4) 宏定宏定义时义时最好在参数两最好在参数两侧侧加括号和整加括号和整个字符串外加括号,防止宏替个字符串外加括号,防止宏替换换出出错错。 。【例】分析以下程序运行结果#define SQ(y) (y)*(y)void main() int a,sq; printf(input a number: ); scanf(%d,&a); sq=160/SQ(a
42、+1); printf(sq=%dn,sq);比较下面【例6】和【例7】程序的不同之处。【例【例6】 】#define SQ(y) y*yvoid main() int a,sq; printf(input a number: ); scanf(%d,&a); sq=SQ(a+1); printf(sq=%dn,sq);运行运行结结果果为为: :input a number:3 sq=7 /*宏代宏代换换后得到:后得到:sq=a+1*a+1; ; */【例【例7】 】#define SQ(y) (y)*(y)void main() int a,sq; printf(input a number
43、: ); scanf(%d,&a); sq=SQ(a+1); printf(sq=%dn,sq);运行运行结结果果为为: :input a number:3 sq=16/*宏代宏代换换后得到:后得到:sq=(a+1)*(a+1); */宏调用和函数调用的主要区别:带参宏函数处理过程不分配内存简单的字符置换分配内存先求实参值,再代入形参处理时间编译时程序运行时参数类型无类型问题定义实参,形参类型程序长度变长不变运行速度不占运行时间调用和返回占时间文件包含:就是将指定文件全部内容包含(装入)到本文件中来。一般格式:#include“文件名文件名” 或或 #include功能:将指定的源文件全部内容
44、包含进来,即装入将指定的源文件全部内容包含进来,即装入#include命令所命令所处位置上使其成为一个程序。处位置上使其成为一个程序。2 文件包含文件包含对文件包含命令还要说明以下几点:1.指定文件名可以用双引号或尖括号括起来。指定文件名可以用双引号或尖括号括起来。例如:例如:#includestdio.h#include二者的区别:二者的区别: 用尖括弧时,直接在库函数头文件目录中查找包含文件,称用尖括弧时,直接在库函数头文件目录中查找包含文件,称为标准方式;为标准方式; 用双引号时,在用户当前目录中寻找包含的文件,若找不到用双引号时,在用户当前目录中寻找包含的文件,若找不到,再按标准方式查
45、找。,再按标准方式查找。一般情况下:包含库函数用尖括弧节省时间;一般情况下:包含库函数用尖括弧节省时间;包含用户自己编写的文件用双引号。包含用户自己编写的文件用双引号。2. 一个一个#include命令只能指定一个被包含文件。命令只能指定一个被包含文件。3. 文件包含允许嵌套,即在一个被包含的文件中又可以包含文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。另一个文件。功能:一个源文件可将另一个源文件的内容全部包含进来一般形式: #include “文件名” 或 #include #include “file2.c”file1.cfile2.cfile1.cfile2.cABA处理
46、过程:预编译时,用被包含文件的内容取代该预处理命令,再对“包含”后的文件作一个源文件编译 直接按标准目录搜索“” 先在当前目录搜索,再搜索标准目录可指定路径文件包含文件包含源文件(*.c)头文件(*.h)宏定义数据结构定义函数说明等文件包含可嵌套#include “file2.c”file1.cAfile3.cC#include “file3.c”file2.c Bfile1.cAfile3.cfile2.c被包含文件内容被包含文件内容/* powers.h */#define sqr(x) (x)*(x)#define cube(x) (x)*(x)*(x)#define quad(x) (
47、x)*(x)*(x)*(x)/*a.c*/#include #include “powers.h#define MAX_POWER 10void main() int n; printf(numbert exp2t exp3t exp4n); printf(-t-t-t-n); for(n=1;n=MAX_POWER;n+) printf(%2dt %3dt %4dt %5dn,n,sqr(n),cube(n),quad(n);例例 文件包含举例文件包含举例【例8】给定半径,计算圆的周长和圆的面积。设计三个包含文件,或称为头文件:第第1个为:个为:myin1.h#include#define
48、PI 3.14159第第2个为:个为:myin2.hfloat getlen(float r) return 2*getsr(r)/r; 第第3个为:个为:myin3.hfloat getsr(float r) return PI*r*r; 主函数的程序如下:主函数的程序如下:#include “myin1.h”#include “myin3.h”#include “myin2.h”void main() float x=3.0;printf(“L=%fn”,getlen(x);printf(“S=%fn”,getsr(x); 因因为为:函数的:函数的调调用关系,用关系,头头文件的包含文件的包
49、含顺顺序序应该应该必必须须是:是:myin1.h,然后然后myin3.h,最后是最后是myin2.h。 。 一般情况下,对源程序进行全部编译。条件编译:根据具体条件和需要只编译源程序中的某些部分为条件编译。条件编译用途:可以用源程序产生不同版本。3 条件条件编译编译1条件编译的三种形式:形式一:形式一:#ifdef 标识符标识符 程序段程序段1#else 程序段程序段2#endif功能:标识符已被功能:标识符已被 #define命令定义过则对程序段命令定义过则对程序段1进行编译;否则进行编译;否则对程序段对程序段2进行编译。进行编译。 形式二:形式二:#ifndef 标识符标识符 程序段程序段
50、1 #else 程序段程序段2 #endif功能:如果标识符未被功能:如果标识符未被#define命令定义过则对程序段命令定义过则对程序段1进行编译,进行编译,否则对程序段否则对程序段2进行编译。进行编译。形式三:形式三:#if 常量表达式常量表达式 程序段程序段1#else 程序段程序段2#endif功能是,如常量表达式的值为真功能是,如常量表达式的值为真(非非0),则对程序段,则对程序段1 进行编译,否进行编译,否则对程序段则对程序段2进行编译。进行编译。#include #define LETTER 0int main( ) char str = C language; char c;