




已阅读5页,还剩19页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第第 6 6 章章 单片机单片机 C C 语言程序设计基础语言程序设计基础 6 16 1 C C 语言与语言与 MCSMCS 5151 用汇编程序设计 MCS 51 系列单片机应用程序时 必须要考虑其存储器结构 尤其必须 考虑其片内数据存储器与特殊功能寄存器正确 合理的使用以及按实际地址处理端口数据 支持 MCS 51 单片机的编程语言 51 汇编 PL M 宏汇编 C51 BASIC 等 C 语言是由 PASCAL 演变而来的一种结构化程序设计语言 主要优点 主要优点 规模最小 关键字 少 书写自由 可移植性好 表达能力强 丰富的数据类型 结构 运算符 结构化好 程序基本单位是函数 可直接控制硬件资源 物理地址访问能力 目标代码质量高 接 近或超过汇编代码 主要缺点 语法定义不严格 出错检查难 运算符优先级复杂 用 C 语言编写 MCS 51 单片机的应用程序 虽然不像用汇编语言那样具体地组织 分配 存储器资源和处理端口数据 但数据类型与变量的定义 必须要与单片机的存储结构相关联 否则编译器不能正确地映射定位 用 C 语言编写单片机应用程序与编写标准的 C 语言程序的不同之处 必须使 C 语言程序 中的数据类型和变量与单片机存储结构及内部资源相对应 其它的语法规定 程序结构及程 序设计方法都与标准的 C 语言程序设计相同 用 C 语言编写的应用程序必须经单片机的 C 语言编译器 简称 C51 转换生成单片机可执行的代码程序 支持 MCS 51 系列单片机的 C 语言编译器有很多种 如 American Automation Auocet BSO TASKING DUNFIELD SHAREWARE KEIL Franklin 等 其中 KEIL Franklin 以它的代码紧凑和使用方便等特点优于其它编译器 本章是针对这种编译器 介绍 MCS 51 单片机 C 语言程序设计 ANSIC 关键字 32 个 auto break case char const continue defaut do double else enum extern float for goto if int long register return short signed sizeof static sturct switch typedef union unsigned void volatile while C51 扩展关键字 13 个 bit sbit sfr sfr16 data bdata idata pdata xdata code interrupt reentrant using 标识符 由字符串 数字 下划线组成 用于表示对象名称 对大小写敏感 标识符第 一个字符必须是字母 用户程序用 或下划线 编译系统用 常见对象 函数 变量 常量 数组 结构 语句等 6 26 2 C51C51 数据类型与运算数据类型与运算 6 2 16 2 1 C51C51 数据类型 数据类型 C51C51 数据类型与标准数据类型与标准 C C 数据类型的最大不同之处 位型 数据类型的最大不同之处 位型 位型 bit 字符型 char 整型 int 长整型 long 浮点型 float 双精度浮点型 double 数组类型 array 结构体类型 struct 共用体 union 枚举 enum 基本类型 构造类型 指针类型 空类型 数据类型 C51 数据类型的取值范围见下表 6 2 26 2 2 C51C51 数据存储类型数据存储类型 1 1 C51C51 存储类型与存储类型与 MCS 51MCS 51 单片机存储空间的对应关系单片机存储空间的对应关系 2 2 C51C51 存储类型及其大小和值域存储类型及其大小和值域 3 3 5151 单片机的存储模式单片机的存储模式 6 2 36 2 3 C51C51 定义定义 SFRSFR 定义方法 两个关键字 定义方法 两个关键字 sfrsfr 和和 sbitsbit 1 1 定义特殊功能寄存器用定义特殊功能寄存器用 sfrsfr 例如 sfr PSW 0 xD0 定义程序状态字 PSW 的地址为 D0H sfr TMOD 0 x89 定义定时器 计数器方式控制寄存器 TMOD 的地址为 89H sfr P1 0 x90 定义 P1 口的地址为 90H 2 2 定义可位寻址的特殊功能寄存器的位用定义可位寻址的特殊功能寄存器的位用 sbitsbit 例如 sbit CY 0 xD7 定义进位标志 CY 的地址为 D7H sbit AC 0 xD0 6 定义辅助进位标志 AC 的地址为 D6H sbit RS0 0 xD0 3 定义 RS0 的地址为 D3H 标准 SFR 在 reg51 h reg52 h 等头文件中已经被定义 只要用文件包含做出申明即可 使用 例如 include reg51 h sbit P1 0 P1 0 sbit P1 2 P1 2 main P1 0 1 P1 2 0 PSW 0 x08 6 2 46 2 4 C51C51 定义直接地址字节定义直接地址字节 单片机内部并行口用 sfr 定义 直接地址字节 外部并行口 的定义如何定义 用指针 定义 指针的定义在 absacc h 头文件中 例如 include absacc h define PA XBYTE 0 xffec main PA 0 x3A 将数据 3AH 写入地址为 0 xffec 的存储单元或 I O 端口 6 2 56 2 5 C51C51 定义位变量定义位变量 使用关键字使用关键字 bitbit 例如 bit lock 将 lock 定义为位变量 bit dirention 将 direction 定义为位变量 注意 注意 不能定义位变量指针 也不能定义位变量数组 6 2 66 2 6 C51C51 运算符 表达式及其规则运算符 表达式及其规则 1 1 算术运算符算术运算符 加法运算符 减法运算符 乘法运算符 除法运算符 模运算或取余运算符 为自增运算符 为自减运算符 2 2 关系运算符 关系运算符 小于 小于等于 大于 大于等于 等于 不等于 3 3 逻辑运算符逻辑运算符 else if a 2 c 20 else if a 3 c 30 else if a 4 c 40 else c 0 2 switch case2 switch case 语句语句 switch case 语句的一般形式如下 switch 表达式 case 常量表达式 1 语句 1 break case 常量表达式 2 语句 2 break case 常量表达式 n 语句 n break default 语句 n 1 例 5 1 单片机 P1 口的 P1 0 和 P1 1 各接一个开关 K1 K2 P1 4 P1 5 P1 6 和 P1 7 各接一只发光二极管 有 K1 和 K2 的不同状态来确定哪个发光二极管被点亮 方法一 方法一 用 if 语句实现 include reg51 h void main char a a P1 a a 屏蔽高 6 位 if a 0 P1 0 x83 else if a 1 P1 0 x43 else if a 2 P1 0 x23 else P1 0 x13 方法二 方法二 用 switch case 语句实现 include reg51 h void main char a a P1 a a 屏蔽高 6 位 switch a case0 P1 0 x83 break case1 P1 0 x43 break case2 P1 0 x23 break case3 P1 0 x13 6 3 26 3 2 循环语句循环语句 1 if1 if 语句和语句和 gotogoto 语句语句 goto 语句只能构成简单循环 与 if 语句一起可以实现当型和直到型循环 构成当型循环 loop if 表达式 语句 goto loop 构成直到型循环 loop 语句 if 表达式 goto loop 2 while2 while 语句语句 while 语句的一般格式 while 表达式 语句 3 do while3 do while 语句语句 do while 语句一般格式 do 语句 while 表达式 4 for4 for 语句语句 for 语句的一般形式 for 表达式 1 表达式 2 表达式 3 语句 举例 举例 例 5 1 的程序只能执行一遍 先用循环程序使其无穷循环下去 用用 gotogoto 语句实现语句实现 include reg51 h void main char a loop a P1 a a 屏蔽高 6 位 switch a case0 P1 0 x83 break case1 P1 0 x43 break case2 P1 0 x23 break case3 P1 0 x13 goto loop 用用 whilewhile 语句实现语句实现 include reg51 h void main char a while 1 a P1 a a 屏蔽高 6 位 switch a 先计算表达式的值 若其值为非 0 则执行内嵌语句 循环 若其值为 0 则退出 while 循环 先执行内嵌的语句 若表达式的值为非 0 时 则循环 当表达式的值为 0 时 则结束循环 执 行 do while 语句下面的语句 While 中的条件恒为 1 表示死循环 case0 P1 0 x83 break case1 P1 0 x43 break case2 P1 0 x23 break case3 P1 0 x13 用用 do whiledo while 语句实现语句实现 include reg51 h void main char a do a P1 a a 屏蔽高 6 位 switch a case0 P1 0 x83 break case1 P1 0 x43 break case2 P1 0 x23 break case3 P1 0 x13 while 1 用用 forfor 语句实现语句实现 include reg51 h void main char a for a P1 a a 屏蔽高 6 位 switch a case0 P1 0 x83 break case1 P1 0 x43 break case2 P1 0 x23 break case3 P1 0 x13 for 中的条件恒为真 表示死循环 6 46 4 数组与指针数组与指针 6 4 16 4 1 数组数组 C 语言具有使用户能够定义一组有序数据项的能力 这组有序的数据称为数组 数组是 一组具有固定数目和相同类型分量的有序集合 各分量的类型为该数组的基本类型 如整型 变量的有序集合称为整型数组 字符型变量的有序集合称为字符型效组 这些整型或字符型 变量是各自所属数组的成员分量 通常称为数组元素 构成一个数组的各元素必须是周一类 型的变量 而不允许在同一数组中出现不同类型的变量 数组数据是用同一个名字的不同下标标识和访问的 数组的下标放在方括号中 是从 O 开始 0 1 2 3 n 的一组有序整数 例如数组 a i 当 i 0 1 2 n 时 a O a 1 a n 分别是数组 a i 的元素 或成员 数组有一维 二维 三维 多维数组之分 常用 的有一维 二维数组和字符数组 1 1 一维数组 一维数组 1 1 一维数组的定义方式 类型说明符 一维数组的定义方式 类型说明符 数组名数组名 整型表达式整型表达式 例 char ch 10 定义了一个一维字符型数组 它有 10 个元素 每个元素由不同的下标 表示 分别为 ch 0 ch 1 ch 2 ch 9 注意 数组的第一个元素的下标为 0 而不是 1 即数组的第一个元素是 ch 0 而不是 ch l 数组的第十个元素为 ch 9 2 2 数组的初始化 数组的初始化 数组中元素的值 可以在程序运行期间 用赋值或输入语句进行设置 但会增加程序的运 行时间 对大型数组而言 这种情况更加突出 对此可以用数组初始化的方法加以解决 所 谓数组初始化 就是在定义说明数组的同时 给数组赋新值 这项工作是在程序的编译中完成 的 对数组的初始化可用以下方法实现 在定义数组时对数组的全部元素设定初值 例 int idata a 6 0 1 2 3 4 5 a 0 0 a 1 1 a 2 2 a 5 5 只对数组的部分元素初始化 例 int idata a 10 O 1 3 4 5 上面定义的 a 数组共有 10 个元素 但花括号内只有 6 个初值 则数组的前 6 个元素被赋予 初值 而后 4 个元素的值为 0 在定义教组时对数组的全部元素不赋初值时 数组的全部元素缺省赋值为 0 例 int ida1a a 0 则 a 0 a 9 全部被赋初值 O 2 2 二维数组 二维数组 1 1 二维数组定义的一般形式 二维数组定义的一般形式 类型说明符类型说明符 数组名数组名 常量表达式常量表达式 常量表达式常量表达式 例 int a 3 5 定义了 3 行 5 列共 15 个元素的二维数组 a 二维数组的存取顺序是 按行存取 先存取第一行元素的第 0 列 1 列 2 列 直到 第一行的最后一列 然后从第二行开始 再取第二行元素的第 0 列 1 列 2 列 直到第 二行的最后一列 如此顺序下去 直到最后一行的最后一列 C 语言允许使用多维数组 有了二维数组的基础 理解掌握多维数组并不困难 例如 float a 2 3 4 定义了一个类型为浮点数的三维数组 2 2 二维数组的初始化 二维数组的初始化 对数组的全部元素赋初值 a 分行给二维数组的全部元素赋初值 例 int a 3 4 1 2 3 4 5 6 7 10 11 12 b 将所有数据写在一个花括号内 按数组的排列顺序对各元素赋初值 例 int a 3 4 1 2 3 4 5 6 7 8 9 10 11 12 对数组中部分元素赋初值 例 int a 3 4 1 5 9 将初值赋给各行的第一列数组元素 例 int a 3 4 1 5 6 学生自己分析 3 3 字符数组 字符数组 基本类型为字符类型的数组称为字符数组 字符数组是用来存放字符的 在字符数组中 一个元素存放一个字符 所以可以用字符数组来存贮长度不同的宇符串 1 1 字符数组的定义 字符数组的定义 字符数组的定义与前面所讲的数组定义的方法类似 例 char a 10 定义 a 为一个有 10 个字符的一维字符数组 2 2 字符数组赋初值 字符数组赋初值 字符数组赋初值的最直接的方法是将各字符逐个赋给数组中的各个元素 例 char a 10 B E I J K L M 0 数组元素 a 0 a 1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 a 9 字符 B E I J K L 0 ASCII 码 42H 45H 49H 20H 4AH 4BH 4CH 4DH 00H 20H C 语言还允许用字符串直接给字符数组置初值 其方法有以下两种形式 char a 10 BEI JINC char a 10 BEI JlNG 用双引号 括起来的一串字符 称为字符串常量 比如 Happy C 编译器会自动地 在字符末尾加上结束符 0 NULL 用单引号 括起来的字符为字符的 ASCII 码值 而不是字符串 一个字符串可以用一维数组来装入 但数组的元素数目一定要比字符多一个 以便 C 编 译器自动在其后面加入结束符 0 若干个字符串可以装人一个二维字符数组中 数组的第一个下标是字符串的个数 第二 个下标定义每个字符串的长度 该长度应当比这批字符串中最长的串多一个字符 用于装入 字符串的结束符 O 例 uchar code msg l7 his is a test n message 1 n message 2 n 这是一个二维数组 第二个下标必须给定 因为它不能从数据表中得到 第一个下标可缺 省由数据常量表决定 本例中实际为 3 4 4 查表 查表 数组的一个非常有用的用途之一就是查表 许多专业人员希望单片机 控制器能对他们提出的公式进行高精度的数学运算 但对大多 数实际应用来说 这并不是完全必要的 在许多嵌入式控制系统应用中 人们更愿意采用表格 而不是数学公式计算 特别是那些对子传感器的非线性转换需要进行补偿的场合 用查表法 如有必要再加上线性插值法 将比采用复杂的曲线拟合所需要的数学方法有效得多 因为表 格查找执行起来速度更快 所用代码较少 表格数据可以事先计算好后装入 EPROM 中 使用内 插法可以增加表值的精度 减少表的长度 数组的使用非常适合于这种查表方法 例 将摄氏温度转换成华氏温度的 define uchar unsigned char uchar code tempt 32 34 36 37 39 41 数组设置在 EPROM 中 uchar x uchar ftoc uchar degc return tempt degc 返回华氏温度值 main x ftoc 5 得到 5C 的华氏温度值 5 5 数组与存贮空间 数组与存贮空间 当程序中设定了一个数组时 C51 编译器就会在系统的存贮空间中开辟一个区域用于存放 该数组的内容 数组就包含在这个由连续存贮单元组成的存贮模块内 对字符数组而言 它占 据了内存中一串连续的字节位置 对整型 int 数组而言 它们将在存贮区中占据一串连续 的字节对的位置 对长型 long 或浮点型 float 数组 一个成员将占有 4 个字节的存贮空间 对于多维数组来说 一个 10 10 10 的三维浮点数组需要大约 4K 字节的存贮空间 而一个 25 25 25 的三维浮点数组就需要大于 64K 的存贮空间 8051 单片机的最大可寻址空间只有 64K 当数组 特别是多维数组中大多数元素没有被有效地利用时 就会浪费大量的存贮空间 8051 单片机这样的嵌入式控制器 不象复用式系统那样拥有大型的存贮区 其存贮资源极为有 限 因此无论如何不能出现不必要地占用 因此在进行 C 语言程开发时要仔细地根据需要选 择数组的大小 6 4 26 4 2 指指 针针 指针是 C 语言中的一个重要概念 也是 C 语言的重要特色之 C 语言区别于其它高级 程序设计语言的主要特点就是它在处理指针时所表现出的能力和灵活性 使用指针的优点 可以有效地表示复杂的数据结构 有效而方便地使用数组 动态地分配存贮器 直接处理内存地址 在调用函数时还能输人或返回多于 1 个的变量值 可以使程序更简洁 紧凑 高效 可以说 不掌握指针就没有掌握 C 语言的精华 1 1 指针的基本概念 指针的基本概念 为了了解指针的基本概念 必须了解数据在内存中是如何存贮和读取过程 程序中定义 了一个变量 C 编译器在编译时就给这个变量在内存中分配相应的内存空间 对一个整型 int 变量分配两字节内存单元 对一个字符型变量分配一字节 char 内存单元 对一个浮点 型变量分配 4 字节内存单元 对于变量要弄清两个概念 一个是变量名 一个是变量值 前者是一个数据的标号 后者 是一个数据的内容 对于内存单元 也要弄清两个概念 个是内存单元的地址 一个是内存单元的内容 前 者是该内存单元的编号 它表示该单元在整个内存中的位置 后者指的是在该内存单元中存 放的数据 在变量与内存单元的对应关系中 变量的变量名与内存单元的地址相对应 变量的变量值 与内存单元的内容相对应 在内存中变量名是不存在的 对变量值的存取是通过地址进行的 存取的方式有两种 直接访问方式直接访问方式 例 int a printf d a 执行过程 根据变量名与内存单元地址的对应关系 找到变量 a 在内存中的位置 即 地址 然后从这个地址开始的两个字节中取出变 a 的值并把它用 printf 语句规定格 式输出 间接访问方式间接访问方式 例 如要存取变量 a 的值时 可以将变量 a 的地址放在另一个内存单元中 访问时 先找到 存放变量 a 的地址的内存单元的地址 从中取出变量 a 的地址 再从这个地址内存单元中取 出变量 a 的值 这种访问方式中实际上用到了指针的概念 为了使用指针进行间接访问 必须弄清关于指针的两个基本概念 即变量的指针和指向变 量的指针变量 简称指针变量 变量的指针 变量的指针 变量的指针就是变量的地址 指向变量的指针变量 指针变量 专门用来存放另一个变量的地址的变量 称为指向变量的指针变 量 简称指针变量 指针变量的值是指针 1 1 指针变量的定义 指针变量的定义 C 语言规定 所有的变量必须先定义后使用 指针变量也不例外 由于它是用来专门存放 地址的 因此必须将它定义为指针类型 指针定义的一般形式为 类型识别符类型识别符 指针变量名 指针变量名 例 int ap float pointer 注意 注意 指针变量名前面的 号表示该变量为指针变量 但指针变量名是 ap 和 pointer 而不是 ap 和 pointer 2 2 指 指针变量的引用针变量的引用 指针变量定义后 就可以使用指针来进行变量间接访问了 例 int x int a 6 b 8 c 10 int ap bp cp ap 此时数组 a 10 的第一个元素 a 0 的地址就赋给了指针变量 app 也就是将指针变量 app 指向数组 a 的第 0 号元素 a 0 aap a 此种引用的方法与 的作用完全相同 但形式上更简单 C 语言规定 数组名可以代表数 组的首地址 可以把指针变量的定义和引用放在一个语句中完成 int aap 方法四 用 typedef 命名一个结构类型 这时结构名就不太重要了 例 typedef struct int ycar char month day DATE 注意 注意 结构类型与结构变量是两个不同的概念 定义一个结构类型时只是给出该结构的组织形 式 并没有给出具体的组织成员 结构名不占用任何存储空间 不能对结构名进行赋值 存 取和运算 而结构变量则是一个结构中的具体成员 编译器会为具体的结构变量名分配确定 的存储空间 因此可以对结构变量名赋值 存取和运算 将 个变量定义为基本类型与将其定义为结构类型的不同之处是 前者只是说明变量的 类型 后者不仅说明该变量为结构类型 同时还要指出该变量所属结构类型的名字 一个结构中的结构元素可以是另外一个结构类型的变量 即可以形成结构的嵌套 例 struct clock unsigned char hour min sec struct mrec unsigned char port1 unsigned char analog 4 struct clock time ml 其中 结构类型 mrec 中的结构元素 time 又是另一个结构类型 clock 的结构变量 形 成了结构的结构 即结构的嵌套 结构的嵌套可以是多层次的 但这种嵌套不能包含其自身 即结构不能自己定义自己 结构中的结构元素可以与结构外其他变量同名 它们各自代表不同的对象 在使用中不 会互相影响 在定义结构变量时 还可以说明它的存储种类 可以 extern auto 和 static 三种形式 2 2 结构变量的引用 结构变量的引用 结构变量定义之后就要考虑对它的引用问题 赋值 存取 运算 对结构变量的引用 是通过所属的结构元素的引用实现的 引用结构元素的一般格式为 结构变量名结构变量名 结构元素结构元素 其中 是存取结构元素的成员运算符 如 d1 month 表示结构变量 d1 中的成员 month 如果 个结构变量中的结构元素又是另外一个结构变量 即出现结构的嵌套时 则 需要采用若干个成员运算符一级一级地找到最低一级的结构元素 而且只能对这个最低级的 结构元素进行访问 例 m1 time min 对结构变量中的各个元素可以像普通变量一样进行赋值 存取和运算 例 d1 year 2006 sum d1 day d2 day d1 month m1 time hour 0 x22 成员运算符的优先级别最高 对于结构变量和结构元素在程序可以直接引用它们的地址 例 scanf d d1 year 结构变量的地址通常用作函数参数 用来传递结构的地址 3 3 结构变量的初值 结构变量的初值 当结构变量为外部全局变量或静态变量时可以在定义结构类型时给它赋初值 但不能给 自动存储种类的动态局部结构变量赋初值 例 struct mepoint unsigned char name 11 unsigned char pressure unsigned char temperature po1 firstpoint 0 x99 0 x64 自动结构变量不能在定义时赋初值 只能在程序执行中用赋值语句为各结构元素分别赋 值 结构变量初值个数必须小于等于结构变量中元素的个数 初值不够时 余下的结构变量 元素以 O 为其初值 如果初值个数多于元素个数时则会导致编译出错 4 4 结构数组 结构数组 在实际使用中 结构变量往往不止一个 通常是将多个相同的结构组成一个结构数组 结构数组的定义方法与结构变量完全一致 例 struct mepoint unsigned char name 11 unsigned char pressure unsigned char temperature struct mepoint po 3 例 结构数组赋初值 struct mepoint unsigned char name 11 unsigned char pressure unsigned char temperature struct mepoint po 3 1stpoint 0 x99 0 x64 2stpoint 0 x10 0 x20 3stpoint 0 x15 0 x18 5 5 结构型指针 结构型指针 a a 结构型指针的概念结构型指针的概念 个指向结构类型变量的指针称为结构型指针 该指针变量的值也是它所指向的结构变 量的起始地址 结构型指针也用来指向结构数组或结构数组中的元素 定义结构型指针的一般形式为 structstruct 结构类型标识符结构类型标识符 结构指针标识符结构指针标识符 其中 结构指针标识符 就是所定义的结构型指针变量的名字 结构类型标识 就是 该指针所指向的结构变量的具体类型名称 例 struct mepoint mp b b 用结构型指针引用结构元素用结构型指针引用结构元素 通过结构型指针引用结构元素的一般形式为 结构指针结构指针 结构元素结构元素 例 mp pressure 等同于 mp pressure 6 6 结构与函数 结构与函数 a a 结构作为函数的参数结构作为函数的参数 一般来说 结构既可作为函数的参敏 也可作为函数的返回值 当结构被用作函数的参 数时 其用法与普通变量作为实参是一样的 其参数传递属于 值传递 方式 程序在进行函数调用时 将整个结构变量作为参数传递给被调函数 系统为形式参数的 结构变量分配存储空间 并从相应的实际参数中取得各个元素的值 函数对形参中各个结构 无素值进行的修改不会对相应的实参结构变量产生任何影响 b b 结构型指针作为函数的参数结构型指针作为函数的参数 当结构较大时 若将该结构作为函数的参数 由于参数传递采用值传递方式 需要较大 的存储空间 堆栈 来将所有的结构元素压栈和出栈 尤其当函数参数是结构数组时 影响 更大 此外还会影响程序的执行速度 实际上可以用结构型指针来作为函数的参数 此时参 数的传递是按地址传递方式进行的 由于采用的是地址传递方式 只需要传递一个地址值 与前者相比 既可节省存储空间 同时还可加快程序的执行速度 缺点是在调用函数时对结 构指针所作的任何变动都会影响到原来的结构变量 5 5 25 5 2 联合 共用体 联合 共用体 联合也是 C 语言中一种构造类型的数据结构 在一个联合中可以包含多个不同类型的数 据元素 各种类型的变量放在同 个地址开始的内存单元中 实现了多层数据覆盖 一方面 有效地提高内存的利用率 另一方面也方便了数据类型间的转换 例 联合中变量的存储方法 float i long j int k char m 1 1 联合的定义 联合的定义 定义联合类型变量的一般形式 unionunion 联合类型名联合类型名 成员表列成员表列 变量表列 变量表列 例 union data float i long j int k char m a b c 也可以将类型定义与变量定义分开 即先定义一个 union data 类型 再将 a b c 定 义为 union data 类型的变量 例 union data float i long j int k char m union data a b c 还可以直接定义联合变量 例 union data float i long j int k char m a b c 由此可见 联合类型与结构类型的定义方法是很相似的 只是将关键字 struct 改成了 union 但是在内存的分配上它们之间有着本质的区别 结构变量所占用的内存长度是其中 各个元素所占用内存长度的总和 而联合变量所占用的时存长度是其中最长的元素的长度 联合变量中的元素分时占用相同的存储空间 2 2 联合变量的引用 联合变量的引用 与结构变量类似 对联合变量的引用也是通过对联合元素的引用来实现的 引用联合元 素的一般格式为 联合变量名 联合元素联合变量名 联合元素 或 联合变量名联合变量名 联台元素联台元素 例 a i 引用联合变量 a 中的 float 型元素 a j 引用联合变量 a 中的 long 型元素 b k 引用联合变量 b 中的 int 型元素 c m 引用联合变量 c 中的 char 型元素 在引用联合元素时 要注意联合变量用法的一致性 因为联合类型中定义的各个不同类 型的元素都可以分时地赋给变量 而所读取变量的值是最近放入的某一元素的值 因此在表 达式中对它进行处理时 必须注意其类型要与表达式所要求的类型保持一致 否则将导致程 序运行出错 联合变量不能整体引用 例如下面的写法就是错误的 printf f a 因为变量 a 可能是 float long int 和 char 三种类型 分别占用不同长度的内存区域 若 在引用时仅写联合变量名 a 系统将难以确定究竟应该输出哪一个联合元素的值 正确的写 法为 printf f a i 联合类型的数据占用的内存空间在某一时刻只能存放一种类型的元素 6 66 6 C51C51 函数函数 6 6 16 6 1 函数的分类与定义函数的分类与定义 函数是 C 语言程序结构中的基本模块 C 语言源程序由若干函数构成 1 1 模块化程序设计 模块化程序设计 C 语言总是由主函数 main 开始 main 函数是实现程序结构流程的特殊函数 它 是程序的起点 如果源程序规模较大 一般应将其分成若干个子程序模块 每个子程序模块 完成一种特定功能 子程序也是用函数来实现的 对于一些需要经常使用的子程序可以按蓝 数来设计 将自己所没计的功能函数作成一个专门的函数库 以供反复调用 C51 编译器中 提供了丰富的运行库函数 用户可以根据需要随时调用 模块化的程序设计方法 可以大大 提高编程效率 2 2 函数的定义 函数的定义 函数分两种 标推库函数和用户自定义函数 标准函数由编译器提供 不需要用户进行 定义 可以直接调用 用户自定义函数由用户编写且能实现特定功能 必须先进行定义之后 才能调用 用户函数定义的一般形式为 函数类型函数类型 函数名 形式参数表 函数名 形式参数表 形式参数说明形式参数说明 局部变量定义局部变量定义 函数体语句函数体语句 函数类型 函数返回值的类型 函数名 自定义函数的名字 形式参数表 列出在主调用函数与被调用函数书之间传递数据的形式参数 形式参 数的类型必须要加以说明 ANSI C 标准允许在形式参数表中直接对形式参数的类型进行说明 自定义函数可以是无参函数 但圆括号不能省略 局部变量定义 对函数内部使用的变量进行定义 函数体语句 完成该函数的特定功能的语句体 空函数也是合法的函数 即语句体为空 只有一对花括号 实际应用中 在进行模块 化程序设计时 结构中的各模块先用空函数表示 相应的功能以后需要时再编写 例 char func1 int x char y Char u u x y return u 注意注意 u 的类型要与返回值的类型一致 void func2 定义一个返回值为空类型的空函数 6 6 26 6 2 函数的调用函数的调用 1 1 函数调用的一般形式 函数调用的一般形式 C 语言程序中函数是可以互相调用的 函数调用就是在一个函数体中引用另一个已经定 义了的函数 前者称为主调用函数 后者称为被调用函数 一般形式为 函数名 实际参数表 函数名 实际参数表 函数名 被调用函数的名称 实际参数表 多个实际参数 各参数间用逗号分隔 实际参数的作用是将它的值传递 给被调用函数中的形式参数 要求实际参数与形式参数必须在个数 类型 顺序严格保持一 致 函数调用主要有三种形式 a 函数语句 在主调函数中将函数调用作为一条语句 例 fun1 无参调用 不许返回值 只完成设计功能 b 函数表达式 在主调函数中将函数调用作为一个运算对象直接出现在表达式中 例 C func1 x y func2 m n 函数表达式中只取被调用函数的返回值 c 函数参数 主调函数将函擞调用作为另一个函数调用的实际参数 例 y func1 func2 m n k 属于嵌套调用 2 2 被调用函数说明 被调用函数说明 函数调用按照 先说明 后调用 的原则进行 a 库函数的说明 在程序的开始处用预处理命令 include 将包含有关函数说明的头文 件 扩展名为 H 包含进来 例 include 头文件 stdio h 中包含有标准库输 入输出函数的说明信息 b 用户由定义函数的说明 被调函数与主调函数在同一个文件中时 一般在主调函数中对被调函数的类型进行说明 一般形式为 类型标识符 被调函数名 形式参数表 注意 注意 函数的说明与函数的定义是两个不同的概念 函数的定义是对函数功能的确立 是一个完整的函数单位 圆括号后面无分号 函数的说明仅表示函数返回值的类型 圆括号 后面有分号 建议 建议 在程序文件的开始处说明所有被凋函数的类型 在主调函数中无须再对被调函数 进行说明 否则一定要先在主调函数中说明被调函数的类型 然后再进行函数调用 C 语言程序中只允许函数的嵌套调用 不允许函数的嵌套定义 例 include int func1 int x inty void main int a b printf input a and b n scanf d d printf func1 d func1 a b int func1 int x int y int c if x y c y return c 6 6 36 6 3 函数的参数与函数的返回值函数的参数与函数的返回值 在进行函数调用时 主调用函数与被调用函数之间具有数据传递关系 这种数据传递是 通过函数的参数实现的 在定义一个函数时 位于函数名后面圆括号中的变量名称为 形式 参数 而在调用函数时 函数名后面括号中的表达式称为 实际参数 形式参救发生在函数调用之前 不占用内存单元也没有值 只有在发生函数调用时它才 被分配内存单元并获得从调用函数中实际参数传递过来的值 函数调用结束后 它所占用的 内存单元也被择放 实际参数可以是常数 也可以是变量或表达式 但要求它们具有确定的值 函数调用时 主调用函数将实际参数的值传递给被调用函数中的形式参数 显然 形式参数与实际参数的 数据类型必须一致 实际参数的传递方式 1 基本类型的实际参数传递 值传递 当函数的参数是基本类型的变量时 主调函数将实际参数的值传递给粥调函数中的形式 参数 值传递是一种单向传递 2 数组类型的实际参数传递 地址传递 当函数的参数是数组类型的变量时 主调函数将实际参数数组的起始地址传递到被调函 数中形式参数的临时存储单元 形式参教通过实际参数传来的地址 直接到主调函数中去存 取相应的数组元素 形式参数的变化会改变实际参数的值 因此地址传递是一种双向传递 3 指针类型的实际参数传递 地址传递 当函数的参数是指针类型的变量时 主调函数将实际参数的地址传递给被被调函数中形 式参数的临时存储单元 执行被调函数时 也是直接到主调函数中去访问实际参数变量 形 式参数的变化会改变实际参数的值 6 6 46 6 4 函数的递归调用与再人函数函数的递归调用与再人函数 在调用一个函数的过程中又间接或直接地调用该函数本身 称为函数的递归调用 例 计算阶乘函数 f n n 先计算 f n 1 n 1 而计算 f n 1 时又可先计算 F n 2 n 2 这就是递归算法 再入函数是可以在函数件内直接或间接调用其自身的 一种函数 显然再入函数是可以进行进归调用的 再入函数的定义如下 函数类型 函数名 形式参数表 reentrant 再人函数可被包括中断服务函数在内的任何函数调用 与非再人函数参数传递和局部变 量的存储分配方法不同 C51 编译器为再人函数生成一个模拟栈 通过这个模拟栈来完成参 数传递和存放局部变量 模拟栈所在的存储器空间根据再入函数存储器模式的不同 可为 DATA PDATA 或 XDATA 空间 当程序中包含有多种存储器模式的再人函数时 C51 编译器为 每种模式单独建立一个模拟栈并独立管理各自的栈指针 再人函数有如下规定再人函数有如下规定 1 再入函数不能传送 bit 类型的参数 也不能定义一个局部位变量 再人函数不能 包括位操作以及 8051 系列单片机的可位寻址区 2 与 PL M51 兼容的函数不能具有 reentrant 属性 也不能调用再入函数 3 在编译时的存储器模式基础上为再人函数在内部或外部存储器中建立一个模拟堆 栈区 称为再人栈 再入函数的局部变量及参数被放在再人栈中 从而保证再人函数的递归 调用 而非再入函数的局部变量被放在再人栈之外的暂存区内 如果对非再人函数进行递归 调用 则上次调用时使用的局部变量及参数将被覆盖 4 同一个程序中可以定义和使用不同存储器模式的再入函数 任意模式的再人函数不 能调用不同模式的再入函数 但可任意调用非再人函数 5 实际参数可以传递给间接调用的再人函数 无再入属性的间接调用函数不能包含调 用参数 但可以使用定义的全局变量来进行参教传递 例 利用函数的递归调用计算整数的阶乘 include fac int n reentrant If n 1 r
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 戏剧摄影创新创业项目商业计划书
- 海洋主题酒店创新创业项目商业计划书
- 海水提锂与锂电池材料创新创业项目商业计划书
- 橡胶作物产业链质量追溯体系创新创业项目商业计划书
- 2025广西南宁市人民路东段小学春季学期教师招聘1人考前自测高频考点模拟试题及答案详解(历年真题)
- 2025河南供销合作总社郑州棉麻工程技术设计研究所招聘9人考前自测高频考点模拟试题及答案详解一套
- 写给客户的感谢信范本
- 教学楼施工项目技术标书范文
- 小学英语自然拼读法教学内容梳理
- 外墙真石漆施工技术方案及材料选用
- 结肠癌肝转移护理查房课件
- 口腔预防保健课件
- 九年级数学上(前两章)试题 Microsoft Word 文档1
- 视频监控系统安装施工方案
- -首次执行衔接问题-行政
- GB/T 95-2002平垫圈C级
- 一起重新构想我们的未来:为教育打造新的社会契约
- GB/T 21073-2007环氧涂层七丝预应力钢绞线
- 压力管道特性表
- 高级会计师评审个人业绩报告(精选9篇)
- 储能型虚拟电厂的建设与思考分析报告
评论
0/150
提交评论