实用c语言程序设计第9章

合集下载

C语言程序设计-第9章

C语言程序设计-第9章

若有以下定义: int a[10],*p,x; 语句a=&x;或a++都是非法的。
在c语言中,虽然不可以改变数组名a中的内容,但可以通过对数 组名加一个整数的办法,来依次表达该数组中不同元素的地址。例 如:(假定已定义k为整型变量); for(k=0;k<10;K+通过表达式:a+k,逐一给出了a数 组中每个元素的地址,使p依次指向a数组中的每一个元素。
一维数组元素的引用
一维数组只带有一个下标,它的引用形式如下: 数组名[下标] /*下标可以是表达式*/ 例如:double x[8]; /*定义了一个双精度型数组,有8个元素*/ 则:x[3]或x[i+3]都是合法的引用形式。
数组概念的相关解释: 1、一个数组元素实际上就是一个变量名,代表内存中的一个存储 单元。一个数组占有一串连续的存储单元。 2、在c语言中,一个数组不能整体引用。在c语言中,数组名中存 放的是一个地址常量,它代表整个数组的首地址。 3、在引用数组元素时,数组元素中下标表达式的值必须是整数, 它的下限为0。在编写程序时保证数组下标不越界是十分重要的。
通过数组的首地址引用数组元素
a是a数组元素的首地址,a(即a+0)的值即等于&a[0];则a+1即等 于&a[1]、a+2的值即等于&a[2]、…、a+9的值即等于&a[9]。 我们知道,可以通过间接访问运算符*来引用地址所在的存储单元, 因此对于数组元素a[0],可以用表达式*&a[0]来引用,也可以用*(a+0) 来引用,对此也可以写成:*a;而对于数组元素a[1],可以用表达式 *&a[1]来引用,也可以用*(a+1)来引用,因为a+1即是a[1] 的地址,使 用间接访问运算符可以引用地址所代表的存储单元,因此,*(a+1)就是 a[1];…;对于数组元素a[9],可以用表达式*&a[9]来引用,也可以用*(a+9) 来引用。因此,可以通过以下语句逐个输出a数组元素中的值: for(k=0;k<10;k++) printf(“%4d”,*(a+k)); 此语句相当于: for(k=0;k<10;k++) printf(“%4d”,a[k]);

《C语言程序设计》课件 《C语言程序设计》课件 第9章-结构类型的使用

《C语言程序设计》课件 《C语言程序设计》课件 第9章-结构类型的使用

printf("%4u%8s%6d%6d%6d\n",t.num,
,t.exama,t.examb,t.total); }
运行结果:
程序 演示
结构体变量 整体的引用
<<C语言程序设计>>
Page 15
9.1 结构类型的根本使用
【随堂练习9-1】
有如下定义: struct job_exam a,b;
<<C语言程序设计>>
Page 19
9.2 用函数处理结构类型数据
例9-6
用函数完成:查找最高 总成绩的考生信息。
【自定义函数】 void find(struct job_exam *p) { *p=s[0];
for(int i=1;i<3;i++) if((*p).total<s[i].total) *p=s[i];

姓名
王虎 李雪 张扬

笔试 成绩 89 82 77

面试 成绩 92 85 96

总成绩
如何处理?
unsigned num; char name[10]; int exama; int examb; int total;
数据特点:
1.各行内数据不尽相同; 2.各行间数据情况相同; 3.以行为单位处理数据.
<<C语言程序设计>>
Page 3
问题引入
某单位招聘考试成绩汇总表
考号
1501 1502 1503
姓名
王虎 李雪 张扬
笔试 成绩
89 82 77
面试 成绩
92 85 96
总成 绩

谭浩强 c语言程序设计(第九章)

谭浩强 c语言程序设计(第九章)
形式1: #ifdef 标识符 程序段1 #else 程序段2 #endif 意义:若该标识 符已被#define 命令定义,则编 译程序段1,否 则编译程序段2 形式2: #ifndef 标识符 程序段1 #else 程序段2 #endif 意义:若该标识
符没有被#define 命令定义,则编 译程序段1,否则 编译程序段2
4
第九章 预处理命令
例:下列程序宏替换。
例 #include <stdio.h> #define N 2 #define M N+1 # define NUM (M+3)*M/2 void main() { int i; for (i=0; i<=NUM; i++); printf(“%d\n”,i); }
第九章 预处理命令
第九章 预处理命令
内容提要: ★宏定义
★文件包含
★条件编译
1
第九章 预处理命令 – 作用:对源程序编译之前做一些处理,生成扩展C 源程序 种类
• 宏定义 • 文件包含 • 条件编译 #define #include #if--#else--#endif等
– 格式:
• “#”开头 • 占单独书写行 • 语句尾不加分号
#ifdef DEBUG printf(“x=%d,y=%d,z=%d\n”,x,y,z); #endif
#define DEBUG
字符串(可为空)
调试时:加上此命令 调试完成后:删去此命令
15
第九章 预处理命令 例5: (选择性地编译不同功能) 。根据需要,有时要把 字母全改为大写输出,另一些时候则要把字母全改为 小写输出。如何适应这两种不同要求。
11
第九章 预处理命令

C语言程序设计_课件_第九章(适于清华谭浩强版)

C语言程序设计_课件_第九章(适于清华谭浩强版)

注意, 注意,对嵌套结构体的引用必须引用
到最末一级。 到最末一级。
struct OBJECT { struct TASK { long plan; long start; long finish; } ti,tj; int count; } ob;
可以使用scanf(“%d”,&ob.ti.plan) 输 可以使用 入数据。但是不能引用ob.ti 。 入数据。但是不能引用
9.2 结构体与数组 9.2.1结构体包含数组 结构体包含数组 由于结构体的成员可以是任何基 本数据类型和另外一个构造类型, 本数据类型和另外一个构造类型,所 以结构体的成员当然可以是数组。 以结构体的成员当然可以是数组。 下面我们说明一个可以描述一个股票 信息的结构体: 信息的结构体:
struct STOCK { char stockname[8]; /*股票名称 */ 股票名称 char stockcode[6]; /*股票代码 */ 股票代码 float price[3]; /*买 入价、卖出价、成交价 */ 买 入价、卖出价、 }; ;
不能混淆结构体名和结构体变量这两 个概念。 个概念。结构体名实际上标识了程序员定 义的一种新的数据类型, 义的一种新的数据类型,编译系统不可能 为结构体名分配内存空间。 为结构体名分配内存空间。只有当变量被 说明为这种由程序员自己定义的数据类型 的结构体时, 的结构体时,编译系统才会为结构体变量 分配存储空间。在不引起混淆的情况下, 分配存储空间。在不引起混淆的情况下, 结构体变量可以简称为结构体。 结构体变量可以简称为结构体。
*/
Struct STOCK sfz={"ShenFaZhan","000001",8.77,8.78,8.78 }; /*用深发展的数据初始化结构体变量 用深发展的数据初始化结构体变量sfz*/ 用深发展的数据初始化结构体变量 printf("\n %s %s ", sfz.stockname, sfz .stockcode); /*输出股票名称和股票代码 */ 输出股票名称和股票代码 for (i=0;i<3;i++) /*输出股票的买入价、卖出价和成交价 输出股票的买入价、 输出股票的买入价 卖出价和成交价*/ printf("%6.2f ", sfz.price[i]); }

第9章_用户建立的数据类型

第9章_用户建立的数据类型

第九章 结构体与共用体一、 教案头: 教学内容第九章 结构体与共用体2 学时认知目标(应知)技能目标(应会): 了解结构体的意义和基本概念 : 能够正确定义和引用结构体变量 : 能够采用指针引用结构体变量 : 能够通过指针引用结构体数组 : 使用结构体变量作函数参数 : 使用结构体指针作函数参数 : 掌握共用体的结构和特点 : 掌握枚举的结构和特点 :typedef 定义类型的意义 : 引用结构体变量成员,引用结构体数组元素的成员 : 结构体指针变量与其所指变量的关系 : 结构体指针变量作函数参数 : 通过指针引用结构体数组 : 通过指针操作字符串 : 掌握共用体变量的定义和引用 : 了解在程序中枚举的应用 : 读程序能够理解typedef 定义的类型 情感目标(理解)教学目标(重点难点)教学目标 : 培养学生自主探究学习、求真务实的品德 : 培养学生细致钻研的学风 : 培养学生努力拓展思维;理论与实际相结合的思维习惯 : 指向结构体的指针 : 结构体数组成员的引用 : 结构体变量作函数参数 : 共用体的存储特性 : 链表的结构特点和操作处理 内容概述 Ø 教材章节: 9.1 结构体类型和结构体变量 9.2 结构体数组 9.3 结构体指针 9.4 用指针处理链表 9.5 共用体类型 9.6 枚举类型 9.7 用typedef命名类型 Ø 教授内容: l 本章讲解了什么是结构体类型,以及如何使用结构类型变量、结构体数组、结构体指针,什么是共用体,它和结构体的异同,以及共用体变量的引用 l 本章还讲解了枚举类型、以及如何使用typedef命名类型 能力训练及任务案例 任务9-1: 结构体变量初始化 任务9-2:结构体数组举例 任务9-3: 指向结构体变量的指针变量示例 任务9-4: 指向结构体数组元素的指针的应用 任务9-5: 结构体变量作函数的参数示例 任务9-6: 指向结构体的指针作函数参数示例任务9-7: 建立静态链表示例 任务9-8:建立动态链表示例 任务9-9: 单链表的输出 任务9-10:共用体示例任务9-11:枚举类型示例 任务9-12:学生信息表(单链表)的基本操作参考资料C 程序设计教程,谭浩强 著,清华大学出版社,2007.07C 程序设计教程学习辅导,谭浩强 编著,清华大学出版社,2007.10 网络学堂:考试系统:无忧网络考试系统 http://10.1.89.100/webexam二、 教学设计 1.专业英语词汇 英文词汇中文名structure 结构member 成员tag 标记function 函数Enumerate 枚举 Define 定义 Union 联合(共用体) create 创建Insert 插入delete 删除Modify / update 修改 2.教学方法 引入结构体概念后采用项目拓展、实例分析引导,递进驱动。

《C语言程序设计》电子课件 第9章课件

《C语言程序设计》电子课件 第9章课件

Your site here
重 庆
9.2文件的打开与关闭

子 工
3.使用文件的方式

文件使用方式
意义

(1)文件使用方式由r、w、a、t、 rt(只读)
打开一个文本文件,只允许读数据

b,+六个字符拼成,各字符的含义
学 院
是:r(read):读;w(write):写; a(append):追加;t(text):文本文
计 算
行管理,把它们的输入、输出等同于对磁盘文件的读和写。

ASCII文件:也称为文本文件,这种文件在磁盘中存放时每
学 院
个字符对应一个字节,用于存放对应的ASCII码。
二进制文件:是按二进制的编码方式来存放文件的。
Your site here

9.1文件概述



工 程
2.文件类型指针

用一个指针变量指向一个文件,这个指针称为文件指针。


9.1文件概述
9.2文件打开与关闭
计 算
9.3文件的读写

9.4文件检测函数


Your site here

第九章 文件



工 程
9.1概述
职 业
“文件”是指一组相关数据的有序集合。这个
学 院
数据集有一个名称,叫做文件名。实际上在前
面的各章中我们已经多次使用了文件,例如源
程序文件、目标文件、可执行文件、库文件



说明文件指针的一般形式如下:
FILE * 指针变量标识符

FILE *fp;

第9章C语言程序设计

第9章C语言程序设计

9.1位运算符
例9.2 按位或程序举例 main() { int a=9,b=5,c; c=a|b; printf("a=%d\nb=%d\nc=%d\n",a,b,c); } 运行结果:
9.1位运算符
9.1.3 按位异或运算
按位异或运算符“^”是双目运算符。其功能是参与运算的两数 各对应的二进位相异或,当两对应的二进位相异时,结果为1。参 与运算数仍以补码出现,例如9^5可写成算式如下: 00001001 ^00000101 00001100 (十进制为12) 9^5结果为12。按位异或通常用来: (1)与0异或,可以保留原值不变。如10^0 00001010 ^ 00000000 00001010
00001001 &00000101 00000001
按位与运算通常用来对某些位清0或保留某些位。例如即想让 某一个字节或多个字节中的位为0。例如:00001001(十进制9) 只要找到这样的数00000110、11110110、00000000等都可以, 即只要和要清零数中每一个二进制位相反即可,或者某一位二进制 位为0和0与结果也是0。
9.1位运算符
9.1.4求反运算
求反运算符~为单目运算符,具有右结合性。 其功能是对参与运算 的数的各二进位按位求反。例如~9的运算为: ~(0000000000001001)结果为:1111111111110110。
9.1位运算符
9.1.5 左移运算
左移运算符“<<”是双目运算符。其功能把“<< ”左边的运算 数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数, 高位丢弃,低位补0。例如: a<<2 指把a的各二进位向左移动2位。 如a=2,十进制00000010,左移2位后为00001000 (十进制8)。 高位左移,低位补0。并且左移一位相当于乘以2的运算,左移两位 相当于乘以4即22。 例9.4 左移程序举例 main() { int a=2; a=a<<2; printf(“a=%d\n”,a); } 运算结果:

C程序设计第九章

C程序设计第九章

包含文件的查找方法: 包含文件的查找方法: #include “文件名” 文件名” 文件名 先在当前工作目录中去查找, 先在当前工作目录中去查找,若找不 到再到指定的标准目录中去查找。 到再到指定的标准目录中去查找。 C编译系统 编译系统, 如:对Turbo C编译系统,先在用户目录下 查找,然后在TC include文件夹中查找 TC\ 文件夹中查找。 查找,然后在TC\include文件夹中查找。 #include <文件名 文件名> 文件名 直接到系统指定的标准目录中去查找。 直接到系统指定的标准目录中去查找。 C编译系统 直接在TC 编译系统, TC\ 如:对Turbo C编译系统,直接在TC\include 文件夹中查找。 文件夹中查找。
main() printf(“%d %d\ ,S(N));} { printf( %d\n”,S(N));} 运行结果为: 运行结果为:17 3+2*3+2*3+2
三、带参数的宏替换与函数的主要区别
⑴函数调用时,先求出实参表达式的值,然后代入 函数调用时,先求出实参表达式的值, 形参。而使用带参的宏只是进行简单的字符替换。 形参。而使用带参的宏只是进行简单的字符替换。 ⑵函数调用是在程序运行时处理的,分配临时的内 函数调用是在程序运行时处理的, 存单元。而宏替换则是在编译时进行的, 存单元。而宏替换则是在编译时进行的,在展开 时并不分配内存单元,不进行值的传递处理, 时并不分配内存单元,不进行值的传递处理,也 没有“返回值”的概念。 没有“返回值”的概念。 宏替换不占运行时间,只占编译时间, ⑶宏替换不占运行时间,只占编译时间,而函数调 用则占运行时间。 用则占运行时间。 函数中函数名及参数均有一定的数据类型, ⑷函数中函数名及参数均有一定的数据类型,而宏 不存在类型问题,宏名及其参数无类型。 不存在类型问题,宏名及其参数无类型。

实用C语言程序的设计教程第9章

实用C语言程序的设计教程第9章
#define SIZE 128

#undef SIZE
#define SIZE 256
实际应用时,由多个源文件组成的程序,在不同的源文 件中可能会出现同一个宏名被定义为不同的内容。若 将这些源文件合并在一起时,就会出现重复宏定义的 错误。为了解决这一问题,可以在每个源程序文件的 末尾将使用过的宏定义均用#undef命令解除。
main() {int x=44,y=67,z=30; float area; area=Area(x,y,z); printf("area=%.2f\n",area); } 程序执行结果: area=147.62 该程序中使用了三个带参数的宏定义,定义中又引用了已定 义的宏和库函数。
(4)带参数宏与函数虽有许多相似之处,但二者本质 上是不同的。 1)函数调用时先计算实参表达式的值,然后将它代入 形参。而使用带参数的宏时,只是进行简单的字符代 换。例如: #define Sqr(x) (x)*(x) 若程序中有语句: y=Sqr (a+b); 对它进行宏展开时并不求a+b的值,而是将实参字符 “a+b”替代形参“x”,所以得到y=( a+b)*( a+b);
printf ("L=%d\nW=%d\nS=%d\n",80,(80+40),(80+40)*80);
由于宏展开只是简单地用定义的宏体去代替宏名而不进 行任何计算。因此,宏定义中若出现表达式时有无圆 括号,效果会是不同的。例如,若将上例中的宏定义 写成:
#define W 80
#define L W+40
3)宏定义命令若一行写不下时,需用“\”表示 下一行继续。例如: #define PRT(a,b) printf("%d\t%d\n",\ (a)>(b)?(a): (b), (a)>(b)?(b): (a)) 9.1.3 解除宏定义 宏定义命令#define出现在程序中函数的外面,一 般放在程序的开头,宏名的有效范围为定义命令 之后到本源文件结束。如果我们想把宏定义的作 用域限制在某一个范围内,可以用#undef命令来 解除已有的宏定义。

C程序设计(第四版)教案第九章

C程序设计(第四版)教案第九章

教案首页第9章用户自己建立数据类型9.1 定义和使用结构体变量9.1.1 自己建立结构体类型用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体例如,一个学生的学号、姓名、性别、年龄、成绩、家庭地址等项,是属于同一个学生的,因此组成一个组合数据,如student_1的变量,反映它们之间的内在联系声明一个结构体类型的一般形式为:struct 结构体名{ 成员表列 };说明:(1)结构体类型并非只有一种,而是可以设计出许多种结构体类型,例如struct Teacherstruct Workerstruct Date等结构体类型各自包含不同的成员(2) 成员可以属于另一个结构体类型。

struct Date{ int month; int day; int year; };struct Stu{ int num;char name[20];char sex;int age;struct Date birthday;char addr[30];};前面只是建立了一个结构体类型,它相当于一个模型,并没有定义变量,其中并无具体数据,系统对之也不分配存储单元。

相当于设计好了图纸,但并未建成具体的房屋。

为了能在程序中使用结构体类型的数据,应当定义结构体类型的变量,并在其中存放具体的数据。

9.1.2 定义结构体类型变量1. 先声明结构体类型,再定义该类型变量声明结构体类型struct Student,可以用它来定义变量struct Student student1,student2;2.在声明类型的同时定义变量struct Student{ int num;char name[20];char sex;int age;float score;char addr[30];} student1,student2;3. 不指定类型名而直接定义结构体类型变量其一般形式为:struct{ 成员表列 }变量名表列;指定了一个无名的结构体类型。

C语言程序设计第9章

C语言程序设计第9章

文件包含命令的典型应用
假设某程序由f1.c、f2.c、f3.c、f4.c四个源文件组成,而且 前3个文件里的函数需调用f4里的函数。按规定,应该在前三个文件中 对f4里的被调函数逐个地进行声明。 按照以往的模式,需要分别在前3个文件里写上被调函数的函数原型。 但是,这种做法有缺点: 当被调函数较多时,工作量较大; 当f4里的被调函数变动后,改动量大。 借助于文件包含命令,容易解决此问题。具体方法是: ①建立一个名为f4.h的文件,其内容为f4.c中各被调函数的函数原型。 ②在前3个文件的开头加上如下一行即可。 #include ”f4.h” 实际上,在调用库函数时,采用了同样的方案。我们在调用数学库 函数时,都会在源文件的开头加上 #include <math.h> 由于math.h中有全部数学库函数的函数原型,所以借助于此文件包含 命令,就实现了对被调库函数的声明。
①S就是一个带参数的宏,它代表 后面含有参数r的字符串。
②对于带参数的宏,在预编译时 不仅要做字符串的替换,而且还 要做参数的替换。
带参数的宏定义命令
#define S(r) 3.14*r*r 3.14*(r)*(r) void main() { float area; area=S(3.6+4.2); printf(”area=%f”,area); }
文件包含命令的两种形式
通常,人们把内容为函数原型、宏定义等相关信息的文件称为头文 件,头文件的扩展名为.h。 C系统为每类库函数都提供了相应的头文件,他们存放在标准头文件 目录中。TC的标准头文件目录为TC安装目录下的INCLUDE子目录(如 D:\TC20\INCLUDE)。 从前面可以看到,文件包含命令有两种使用形式,二者略有差别。 如果使用 #include <文件名> 则在预编译时,C系统会到标准头文件目录中去寻找被包含文件。这种 包含方式也称为标准方式。 如果使用 #include ”文件名” 则在预编译时,C系统会先在当前目录中寻找被包含文件,若找到,则 包含该文件;若找不到,再按标准方式查找。 如果为调用库函数而包含相应的头文件,最好用标准方式,这样会 节省时间。最后,文件包含可以嵌套 。

《C语言程序设计理论与实践》PPT 第九章

《C语言程序设计理论与实践》PPT 第九章

9.1.4 结构体指针
1.结构体包含指针 如:struct test {
int data;
int *p;
}; 第一个元素是一个整型变量,第二个是指向整 型变量的指针。在使用该指针时,首先必须指向确定 变量。
2.指向结构体的指针 一个指针变量当用于指向一个结构体变量时, 称之为结构体指针变量。其值为结构体变量的首地址。 一般形式: struct 结构体 * 结构体指针变量名 例:struct student * pstu 一般访问形式:
该结构体有4个成员,括号后的分号不能少。结 构体变量占用内存空间为所有类型之和。
9.1.1 结构体变量
1.先定义结构体类型,然后在定义结构体变量。 struct 结构体名 {
成员名; }; struct 结构体名 变量名1,变量名2...; 例如:struct date{
int year; int month;int day;}; struct date birthday;
9.3 枚举类型
实际生活中,很多都被限定在一定范围。如: 一个星期只有七天,一年只有十二个月。如果将这些 抽象定义成整型显然不妥当。为此,C语言提供了 “枚举”类型,它将变量的值一一列举出来,变量的 值就限定在列举出来的范围。
定义形式:
enum 枚举名 { 枚举元素表(用逗号隔开) }; 如:enum weekday{ sun,mon,tue,wed,thu,fri,sat }; 再使用 enum weekday去定义该枚举变量,如: enum weekday day;
如同整型数组,当把数组名赋值给某一指针, 其指向就是整个数组的首地址。
9.1.6 用typedef进行类型定义
C语言不仅提供了丰富的数据类型,而且还允许 由用户自己定义类型说明符,也就是说允许由用户为 数据类型取“别名”。类型定义符typedef即可用来 完成此功能。

C语言程序设计教程 第9章

C语言程序设计教程 第9章

3. fscanf函数 该函数的功能是,从指定的文件中格式化读数据。 其一般形式为 fscanf(文件指针,格式控制,地址表); 这个函数与格式输入函数scanf很相似,它们的区别 就在于,scanf函数是从键盘输入数据,而fscanf函数是从 文件读入数据,因此在fscanf函数参数中多了一个文件指 针,用于指出从哪个文件读入数据。
9.4 文件的定位
1.rewind函数 该函数的功能是,将文件的读写指针移动到文件的 开头。其形式为 rewind(fp); 其中fp是已经打开的文件指针。
2.fseek函数 该函数的功能是,将文件的读写指针移动到指定 的位置。其形式为
fseek(文件指针,偏移量,起始位置);
其中各参数的意义如下: 起始位置是指移动文件读写指针的参考位置,它 有以下3个值: SEEK_SET或0 表示文件首
SEEK_CUR或1
SEEK_END或2
表示当前读写的位置
表示文件尾
3.ftell函数 该函数的功能是,返回文件的当前读写位置(出错 返回-1L)。其形式为 ftell(fp);
9.5 程序举例
例9.6 统计文件letter.txt中的字符个数。 C程序如下:
#include "stdio.h" main() { long count=0; FILE *fp; if ((fp=fopen("letter.txt","r+"))==NULL) { printf("cannot open this file!\n"); exit(0); } fgetc(fp); while(!feof(fp)) { fgetc(fp); count=count+1; } printf("count=%ld\n",count); fclose(fp); }
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

9.1.3 解除宏定义 宏定义命令#define出现在程序中函数的外面,一般放在 程序的开头,宏名的有效范围为定义命令之后到本源 文件结束。如果我们想把宏定义的作用域限制在某一 个范围内,可以用#undef命令来解除已有的宏定义。 (1)解除宏定义命令的一般形式: #undef 宏名 其中,宏名必须是此前已经定义过的。其功能是解除前 面定义过的宏,终止其作用域,使之不再起作用。例 如:
对它进行宏展开时并不求a+b的值,而是将实参字符 “a+b”替代形参“x”,所以得到y=( a+b)*( a+b); 2)函数调用是在程序运行中处理的,临时给它分配存储 单元。而宏代换是在编译时进行的,并不给它分配存 储单元,不进行值的传递,也没有返回值。 3)函数的形参和实参都要求定义类型,而且二者要求一 致,若二者不一致时,还要进行类型转换。而对宏不 存在此类问题,宏无类型,参数也无类型,它们都仅 是一种符号代表,宏展开时只是进行对应符号的代换。 4)在程序中使用宏时,宏展开后源程序会变长。而函数 调用不会使源程序变长。 5)宏代换不占程序运行时间,只占编译时间。而函数调 用则占运行时间(分配单元、保留现场、值传递、返 回)。
【例9-1】无参宏定义举例。 #define PI 3.1415926 main() {float r=6; float l,s,v; l=2.0*PI*r; s=PI*r*r; v=3.0/4*PI*r*r*r; printf ("r=%.2f\nl=%.2f\ns=%.2f\nv=%.2f\n",r,l,s,v); }
(1)用无参宏定义来定义符号常量,例如: #define PI 3.1415926 #define MAX 100 #define SIZE 10 #define NO 0 #define YES 1 在这些例子中,用常量的含义作为常量的符号名称,可以使 程序员见名释义,提高程序的可读性,特别是这些常量的值 需要改变时,只须改变#define命令行,一改全改。
程序执行结果: r=6.00 l=37.70 s=113.10 v=508.94 (2)在宏定义中,可以引用已定义过的宏名,即用已定 义的宏定义另外的宏。 【例9-2】定义宏时利用已定义过的宏名举例。 #define W 80
#define L (W+40) #define S L*W main() { printf("L=%d\nW=%d\nS=%d\n",L,W,S); } 程序执行结果: W=80 L=120 S=9600
经过宏展开后,S就被80+40*80代替,这显然不是本题所 计算的面积。 (3)对于程序中用双引号括起来的字符串中的字符,即 使与宏名相同,也不进行替换。例如例9.2中的printf函 数内,双引号中L、W、S没有被替换,仅替换了后边 的不在双引号中的L、W、S。 (4)#define命令出现在函数的外面,宏名的有效范围为 从定义命令到该源程序结束。通常宏定义命令都写在 源程序的开头位臵,函数之前,作为文件的一部分, 其在此文件范围内有效
在程序中若有带参数的宏(如V(4,2,8)),则按 #define命令行中指定的字符串从左到右进行置换。 对于字符串中的形式参数(如l,w,h),逐一用对应的 实参(如4,2,8)进行替换,对于字符串中的非形式参 数字符(如l*w*h中的星号),则原样保留。这样就 形成了宏代换的字符串(如4*2*8)。
(2)在带参数宏定义中,也可以引用已定义过的宏定义。 例如: #define PI 3.1415926 #define S (r) PI*r*r #define V(r) 3.0/4* S(r)*r 预处理对每个宏名展开代换,直到程序中不再有宏名为 止。例如,对于下列宏引用: v=V(5); 其宏展开如下: v=3.0/4* S(5)*5; v=3.0/4* PI*5*5*5; v=3.0/4*3.1415926*5*5*5;
2)函数调用是在程序运行中处理的,临时给它分 配存储单元。而宏代换是在编译时进行的,并不给 它分配存储单元,不进行值的传递,也没有返回值。 3)函数的形参和实参都要求定义类型,而且二者 要求一致,若二者不一致时,还要进行类型转换。 而对宏不存在此类问题,宏无类型,参数也无类型, 它们都仅是一种符号代表,宏展开时只是进行对应 符号的代换。 4)在程序中使用宏时,宏展开后源程序会变长。 而函数调用不会使源程序变长。 5)宏代换不占程序运行时间,只占编译时间。而 函数调用则占运行时间(分配单元、保留现场、值 传递、返回)。
第9章编译预处理
编译预处理是对C源程序编译前进行的一些预加工,如臵换源程序文 件中的特定表示符,或是把指定的头文件嵌入到被编译的源文件里的操 作等。这些操作是通过命令实现的,即预编译命令,用以改变程序设计 环境,提高编程效率。这也是C语言的一个重要特点。 编译预处理命令主要有三种,即宏定义、文件包含和条件编译。 编译预处理命令是由ANSI C统一规定的,但是它不属于C语言语句的范 畴,所使用的命令单词也不是C语言的保留字。为了表示区别,所有编 译预处理命令均以“#”号开头,各占一个单独的书写行,末尾不用分号 作为结束符。如果一行书写不下,可用反斜线“\”和回车键来结束本行, 然后在下一行继续书写。
(2)#undef的另一个作用是重新进行宏定义。在C语言 中宏不能重复定义,即程序中不能定义同名的宏。例 如,在程序开头SIZE是128,到程序中的某一个地方需 要定义SIZE的值是256。要对SIZE定义新值就必须先用 #undef SIZE命令解除原来的定义,即: #define SIZE 128 … #undef SIZE #define SIZE 256 实际应用时,由多个源文件组成的程序,在不同的源文 件中可能会出现同一个宏名被定义为不同的内容。若 将这些源文件合并在一起时,就会出现重复宏定义的 错误。为了解决这一问题,可以在每个源程序文件的 末尾将使用过的宏定义均用#undef命令解除。
(5)在使用带参数宏定义时,要注意下列几点。 1)宏名与带参数的圆括号之间应紧连在一起,中间不能 有空格,否则会出错,例如,如果将上例误写为: #define Sqr (x) (x)*(x) 若程序中有: y=Sqr(5); 则宏展开后为: y=(x)(x)*(x);
这是一个错误语句。原因是在#define中的Sqr和(x)之间有 一个空格,Sqr被定义成一个符号常量,即定义宏名为 Sqr,宏体为(x) (x)*(x),因此才导出这一错误语句。 2)在宏展开后容易引起误解的表达式,在宏定义时,应 将表达式用圆括号括起来。例如: #define S(a,b) a+b 若程序中有语句: v=S(2,3)*5;
经过宏展开后,printf函数中的输出项W被80代替,L被 (80+40)代替,S被(80+40)*80代替,printf函数调 用语句展开为: printf ("L=%d\nW=%d\nS=%d\n",80,(80+40),(80+40)*80); 由于宏展开只是简单地用定义的宏体去代替宏名而不进 行任何计算。因此,宏定义中若出现表达式时有无圆 括号,效果会是不同的。例如,若将上例中的宏定义 写成: #define W 80 #define L W+40 #define S L*W
9.1
宏定义
宏定义是用一个指定的标识符(名字)代表一个字符串,从而使 程序更加简洁易读。 9.1.1 不带参数的宏定义 不带参数宏定义的一般形式如下: #define 标识符 字符串 其中,#define是宏定义的命令,标识符和字符串之间用空格分开。 标识符又称为宏名,通常用大写字母表示。字符串又称为宏体, 一般是常数、关键字、语句、表达式,也可以是空白。其功能是 将宏体字符串符号化。在程序中凡出现该宏名的位臵,经编译预 处理的加工,都被替换成为对应的宏体字符串,称之为宏展开。 由于宏定义不是C语、b、c求三角形的面积 #include <math.h> #define S(a,b,c) (a+b+c)/2 #define Srt(a,b,c) S(a,b,c)*(S(a,b,c)-a)*(S(a,b,c)-b)*(S(a,b,c)-c) #define Area(a,b,c) sqrt(Srt(a,b,c)) main() {int x=44,y=67,z=30; float area; area=Area(x,y,z); printf("area=%.2f\n",area); }
【例9-3】带参宏定义举例。 #define PI 3.1415926 #define S (r) PI*r*r main() {float a,area; a=5.6; area=S(a); printf ("r=%f\narea=%f\n",a,area); } 程序执行结果: r=5.600000 area=98.520340
则该语句展开后为: v=2+3*5; 所以,原来的先计算2+3,则变成了先计算3*5。如果将 宏定义中的表达式用圆括号括起来,即改为: #define S(a,b) (a+b) 就不会出现这种问题。也可以给实参加圆括号来解决这 种问题。 3)宏定义命令若一行写不下时,需用“\”表示下一行继 续。例如: #define PRT(a,b) printf("%d\t%d\n",\ (a)>(b)?(a): (b), (a)>(b)?(b): (a))
9.2
文件包含
文件包含是指一个源程序文件将另一个文件全部包含进 来,它是由C语言预编译命令#include来实现的。其一 般形式为: #include <文件名> 或 #include “文件名” 其中,文件名是指被包含的文件名称。被包含文件可以 是存在于系统中的标题文件,即头文件中,也可以存 在于用户编制的程序文件。作为头文件常以“.h”为文 件扩展名。
相关文档
最新文档